From decb634f671431e880bd78ea71006d4a5b93d61b Mon Sep 17 00:00:00 2001 From: vivekCometChat Date: Tue, 28 Apr 2026 15:42:59 +0530 Subject: [PATCH 01/61] v6-android-beta2 docs --- docs.json | 106 +- .../android/v6/ai-assistant-chat-history.mdx | 357 ++++++ ui-kit/android/v6/ai-features.mdx | 2 +- ui-kit/android/v6/call-features.mdx | 4 +- ui-kit/android/v6/calling-integration.mdx | 4 +- ui-kit/android/v6/component-styling.mdx | 624 +++++++++ ui-kit/android/v6/core-features.mdx | 30 +- ui-kit/android/v6/getting-started-jetpack.mdx | 2 +- ui-kit/android/v6/getting-started-kotlin.mdx | 2 +- ui-kit/android/v6/getting-started.mdx | 6 +- ui-kit/android/v6/guide-ai-agent.mdx | 419 ++++++ ui-kit/android/v6/guide-overview.mdx | 6 +- ui-kit/android/v6/guide-search-messages.mdx | 235 ++++ ui-kit/android/v6/search.mdx | 1136 +++++++++++++++++ 14 files changed, 2868 insertions(+), 65 deletions(-) create mode 100644 ui-kit/android/v6/ai-assistant-chat-history.mdx create mode 100644 ui-kit/android/v6/guide-ai-agent.mdx create mode 100644 ui-kit/android/v6/guide-search-messages.mdx create mode 100644 ui-kit/android/v6/search.mdx diff --git a/docs.json b/docs.json index a064bb587..3e9836fb7 100644 --- a/docs.json +++ b/docs.json @@ -916,7 +916,7 @@ "icon": "/images/icons/react.svg", "versions": [ { - "version": "v5\u200e", + "version": "v5‎", "groups": [ { "group": " ", @@ -1027,7 +1027,7 @@ ] }, { - "version": "v4\u200e", + "version": "v4‎", "groups": [ { "group": " ", @@ -1190,7 +1190,7 @@ ] }, { - "version": "v3\u200e", + "version": "v3‎", "groups": [ { "group": " ", @@ -1212,7 +1212,7 @@ ] }, { - "version": "v2\u200e", + "version": "v2‎", "groups": [ { "group": " ", @@ -1240,7 +1240,7 @@ "icon": "/images/icons/swift.svg", "versions": [ { - "version": "v5\u200e\u200e", + "version": "v5‎‎", "groups": [ { "group": " ", @@ -1347,7 +1347,7 @@ ] }, { - "version": "v4\u200e\u200e", + "version": "v4‎‎", "groups": [ { "group": " ", @@ -1511,7 +1511,7 @@ ] }, { - "version": "v3\u200e\u200e", + "version": "v3‎‎", "groups": [ { "group": " ", @@ -1533,7 +1533,7 @@ ] }, { - "version": "v2\u200e\u200e", + "version": "v2‎‎", "groups": [ { "group": " ", @@ -1561,7 +1561,7 @@ "icon": "/images/icons/android.svg", "versions": [ { - "version": "v5\u200e\u200e\u200e", + "version": "v5‎‎‎", "groups": [ { "group": " ", @@ -1678,7 +1678,7 @@ ] }, { - "version": "v6\u200e\u200e\u200e", + "version": "v6‎‎‎", "groups": [ { "group": " ", @@ -1750,7 +1750,9 @@ "ui-kit/android/v6/incoming-call", "ui-kit/android/v6/outgoing-call", "ui-kit/android/v6/call-buttons", - "ui-kit/android/v6/call-logs" + "ui-kit/android/v6/call-logs", + "ui-kit/android/v6/search", + "ui-kit/android/v6/ai-assistant-chat-history" ] }, { @@ -1772,7 +1774,9 @@ "ui-kit/android/v6/guide-group-chat", "ui-kit/android/v6/custom-text-formatter-guide", "ui-kit/android/v6/mentions-formatter-guide", - "ui-kit/android/v6/shortcut-formatter-guide" + "ui-kit/android/v6/shortcut-formatter-guide", + "ui-kit/android/v6/guide-search-messages", + "ui-kit/android/v6/guide-ai-agent" ] }, "ui-kit/android/v6/architecture-data-flow", @@ -1791,7 +1795,7 @@ ] }, { - "version": "v4\u200e\u200e\u200e", + "version": "v4‎‎‎", "groups": [ { "group": " ", @@ -1948,7 +1952,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e", + "version": "v3‎‎‎", "groups": [ { "group": " ", @@ -1973,7 +1977,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e", + "version": "v2‎‎‎", "groups": [ { "group": " ", @@ -2002,7 +2006,7 @@ "icon": "/images/icons/flutter.svg", "versions": [ { - "version": "v5\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎", "groups": [ { "group": " ", @@ -2106,7 +2110,7 @@ ] }, { - "version": "v6\u200e\u200e\u200e\u200e", + "version": "v6‎‎‎‎", "groups": [ { "group": " ", @@ -2220,7 +2224,7 @@ ] }, { - "version": "v4\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎", "groups": [ { "group": " ", @@ -2781,7 +2785,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎", "groups": [ { "group": " ", @@ -2802,7 +2806,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎", "groups": [ { "group": " ", @@ -2829,7 +2833,7 @@ "icon": "/images/icons/vuejs.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -2922,7 +2926,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -2944,7 +2948,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -2976,7 +2980,7 @@ "icon": "/images/icons/js.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3068,7 +3072,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3191,7 +3195,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3294,7 +3298,7 @@ "icon": "/images/icons/react.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3382,7 +3386,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3496,7 +3500,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3598,7 +3602,7 @@ "icon": "/images/icons/swift.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3687,7 +3691,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3805,7 +3809,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3912,7 +3916,7 @@ "icon": "/images/icons/android.svg", "versions": [ { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4013,7 +4017,7 @@ ] }, { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4107,7 +4111,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4227,7 +4231,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4330,7 +4334,7 @@ "icon": "/images/icons/flutter.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4422,7 +4426,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4513,7 +4517,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5079,7 +5083,7 @@ "icon": "/images/icons/js.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5121,7 +5125,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5205,7 +5209,7 @@ "icon": "/images/icons/react.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5246,7 +5250,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5320,7 +5324,7 @@ "icon": "/images/icons/swift.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5361,7 +5365,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5435,7 +5439,7 @@ "icon": "/images/icons/android.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5475,7 +5479,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5549,7 +5553,7 @@ "icon": "/images/icons/flutter.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5589,7 +5593,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -9616,7 +9620,7 @@ "metatags": { "charset": "UTF-8", "viewport": "width=device-width, initial-scale=1.0", - "description": "Learn how to integrate, customize, and scale real-time chat using CometChat\u2019s UI Kits, SDKs, and widgets across popular frameworks. Get step-by-step guides, best practices, and implementation details to build production-ready chat experiences.", + "description": "Learn how to integrate, customize, and scale real-time chat using CometChat’s UI Kits, SDKs, and widgets across popular frameworks. Get step-by-step guides, best practices, and implementation details to build production-ready chat experiences.", "language": "en" } }, @@ -9625,4 +9629,4 @@ "redirect": true } } -} +} \ No newline at end of file diff --git a/ui-kit/android/v6/ai-assistant-chat-history.mdx b/ui-kit/android/v6/ai-assistant-chat-history.mdx new file mode 100644 index 000000000..00d8de003 --- /dev/null +++ b/ui-kit/android/v6/ai-assistant-chat-history.mdx @@ -0,0 +1,357 @@ +--- +title: "AI Assistant Chat History" +description: "Displays the conversation history between users and an AI assistant for easy review of past interactions." +--- + + +```json +{ + "component": "CometChatAIAssistantChatHistory", + "package": "com.cometchat.uikit.kotlin.presentation.aiassistantchathistory", + "xmlElement": "", + "description": "Displays the conversation history between users and an AI assistant for easy review of past interactions.", + "primaryOutput": { + "method": "setOnItemClickListener", + "type": "OnItemClickListener" + }, + "methods": { + "data": { + "setUser": { + "type": "User", + "required": true, + "note": "User must have role set to @agentic" + } + }, + "callbacks": { + "setOnItemClickListener": "OnItemClickListener", + "setOnItemLongClickListener": "OnItemLongClickListener", + "setOnNewChatClickListener": "OnClick", + "setOnCloseClickListener": "OnClick" + }, + "visibility": { + "setErrorStateVisibility": { "type": "int (View.VISIBLE | View.GONE)", "default": "View.VISIBLE" }, + "setEmptyStateVisibility": { "type": "int (View.VISIBLE | View.GONE)", "default": "View.VISIBLE" } + }, + "style": { + "setStyle": { + "type": "@StyleRes int", + "parent": "CometChatAIAssistantChatHistoryStyle" + } + } + }, + "events": [], + "sdkListeners": [] +} +``` + + + +## Quick Start + +1. Open your layout XML file. +2. Add the `CometChatAIAssistantChatHistory` XML element: + +```xml lines + +``` + +> **What this does:** Adds the `CometChatAIAssistantChatHistory` component to your layout. It fills the available width and height and renders the AI assistant chat history list. + +3. In your Activity or Fragment, create a `User` object with the role set to `@agentic` and pass it to the component: + + + + +```kotlin lines +val user = User() +user.uid = "userId" +user.name = "User Name" +user.role = "@agentic" // User role must be @agentic to use AI Assistant features + +binding.cometChatAiAssistantChatHistory.setUser(user) +``` + + + + +```kotlin lines +import com.cometchat.uikit.compose.presentation.aiassistantchathistory.ui.CometChatAIAssistantChatHistory + +@Composable +fun AIAssistantChatHistoryScreen() { + CometChatAIAssistantChatHistory( + modifier = Modifier.fillMaxSize(), + onCloseClick = { /* handle close */ }, + onNewChatClick = { /* handle new chat */ }, + onItemClick = { message -> /* handle item click */ } + ) +} +``` + + + + + +> **What this does:** Creates a `User` object with the `@agentic` role and sets it on the `CometChatAIAssistantChatHistory` component. This is required for the component to fetch and display the AI assistant chat histories for that user. + +4. Build and run your app. +5. Verify that the AI assistant chat history list appears with past conversation items. + + + + + +## Core Concepts + +- **`CometChatAIAssistantChatHistory`**: The main component class that renders the AI assistant chat history list. It is a Composite Component that can be launched via button clicks or any user-triggered action. +- **Actions**: Callbacks such as `setOnItemClickListener`, `setOnItemLongClickListener`, `setOnNewChatClickListener`, and `setOnCloseClickListener` that let you respond to user interactions. +- **Style**: XML theme styles applied via `setStyle()` to customize colors, fonts, and visual appearance of the chat history. +- **Functionality**: Methods like `setUser`, `setErrorStateVisibility`, and `setEmptyStateVisibility` that configure the component's behavior and state visibility. + +## Actions and Events + +### Callback Methods + +What you're changing: How the component responds to user interactions such as taps, long-presses, new chat clicks, and close clicks. + +- **Where**: Activity or Fragment where you hold a reference to `CometChatAIAssistantChatHistory`. +- **Applies to**: `CometChatAIAssistantChatHistory`. +- **Default behavior**: Predefined actions execute automatically when the user interacts with the component. +- **Override**: Call the corresponding setter method to replace the default behavior with your own logic. + +#### `setOnItemClickListener` + +Function invoked when a chat history item is clicked, used to open an AI assistant chat screen. + + + + +```kotlin YourActivity.kt lines +binding.cometchatAiAssistantChatHistory.setOnItemClickListener { view, position, message -> + + } +``` + + + +```kotlin lines +import com.cometchat.uikit.compose.presentation.aiassistantchathistory.ui.CometChatAIAssistantChatHistory + +CometChatAIAssistantChatHistory( + onItemClick = { message -> + Log.i(TAG, "Item clicked: ${message.id}") + } +) +``` + + + +> **What this does:** Replaces the default item-click behavior. When a user taps a chat history item, your custom lambda executes instead of the built-in navigation. + +#### `setOnItemLongClickListener` + +Function executed when a chat history item is long-pressed, allowing additional actions like delete or block. + + + + +```kotlin YourActivity.kt lines +binding.cometchatAiAssistantChatHistory.setOnItemLongClickListener { view, position, message -> + + } +``` + + + +```kotlin lines +CometChatAIAssistantChatHistory( + onItemLongClick = { message -> + Log.i(TAG, "Item long clicked: ${message.id}") + } +) +``` + + + +> **What this does:** Replaces the default long-press behavior. When a user long-presses a chat history item, your custom lambda executes. + +#### `setOnNewChatClickListener` + +Function triggered when the new chat button is clicked, used to start a new conversation with the AI assistant. + + + + +```kotlin YourActivity.kt lines +binding.cometchatAiAssistantChatHistory.setOnNewChatClickListener { + + } +``` + + + +```kotlin lines +CometChatAIAssistantChatHistory( + onNewChatClick = { + Log.i(TAG, "New chat clicked") + } +) +``` + + + +> **What this does:** Replaces the default new-chat-click behavior. When the user taps the new chat button, your custom logic runs instead of the built-in action. + +#### `setOnCloseClickListener` + +Function activated when the close button is clicked, used to exit the chat history view. + + + + +```kotlin YourActivity.kt lines +binding.cometchatAiAssistantChatHistory.setOnCloseClickListener { + + } +``` + + + +```kotlin lines +CometChatAIAssistantChatHistory( + onCloseClick = { + Log.i(TAG, "Close clicked") + } +) +``` + + + +> **What this does:** Replaces the default close-click behavior. When the user taps the close button, your custom logic runs instead of the built-in exit action. + +- **Verify**: After setting an action callback, trigger the corresponding user interaction (tap, long-press, new chat, close) and confirm your custom logic executes instead of the default behavior. + +## Styling + +What you're changing: The visual appearance of the AI Assistant Chat History component using XML theme styles. + +- **Where**: `themes.xml` for style definitions, and your Activity/Fragment for applying the style. +- **Applies to**: `CometChatAIAssistantChatHistory`. +- **Default behavior**: The component uses its default style. +- **Override**: Define a custom style in `themes.xml`, then call `setStyle()` on the component. + + + + + +- **Code**: + +```xml themes.xml lines + + + +``` + +> **What this does:** Defines a custom style `CustomAIAssistantChatHistoryStyle` that sets the background color to `#FFFAF6` for the component, header, new chat area, date separator, and items. It applies a Times New Roman font to the header, new chat text, date separator, and item text. A helper style `textStyleTimesNewRoman` defines the font family. + + + + +```kotlin lines +binding.cometchatAiAssistantChatHistory.setStyle(R.style.CustomAIAssistantChatHistoryStyle); +``` + + + +```kotlin lines +import com.cometchat.uikit.compose.presentation.aiassistantchathistory.style.CometChatAIAssistantChatHistoryStyle + +CometChatAIAssistantChatHistory( + style = CometChatAIAssistantChatHistoryStyle.default().copy( + chatHistoryBackgroundColor = Color(0xFFEDEAFA) + ) +) +``` + + + +> **What this does:** Applies the `CustomAIAssistantChatHistoryStyle` theme to the `CometChatAIAssistantChatHistory` component, changing the background colors and fonts. + +To know more such attributes, visit the [attributes file](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_ai_assistant_chat_history.xml). + +- **Verify**: The chat history component displays with the `#FFFAF6` background color and Times New Roman font for header text, new chat text, date separator text, and item text. + +## Functionality + +What you're changing: Small functional customizations such as setting the user and toggling visibility of UI states. + +- **Where**: Activity or Fragment where you hold a reference to `CometChatAIAssistantChatHistory`. +- **Applies to**: `CometChatAIAssistantChatHistory`. +- **Default behavior**: All UI states are visible with default settings. +- **Override**: Call the corresponding method on the component instance. + +| Methods | Description | Code | +| --- | --- | --- | +| `setUser` | Sets the user whose chat histories with the AI assistant need to be fetched. This is a required property for the component to function. The user's role must be `@agentic`. | `.setUser(user);` | +| `setErrorStateVisibility` | Toggles the visibility of the error state of the component | `.setErrorStateVisibility(View.GONE);` | +| `setEmptyStateVisibility` | Toggles the visibility of the empty state of the component | `.setEmptyStateVisibility(View.GONE);` | + +- **Verify**: After calling `setUser(user)`, confirm the component fetches and displays the AI assistant chat histories for that user. After calling a visibility method, confirm the corresponding UI state is shown or hidden. + +## Customization Matrix + +| What you want to change | Where | Property/API | Example | +| --- | --- | --- | --- | +| Component background color | `themes.xml` | `cometChatAIAssistantChatHistoryBackgroundColor` | `#FFFAF6` | +| Header background color | `themes.xml` | `cometChatAIAssistantChatHistoryHeaderBackgroundColor` | `#FFFAF6` | +| Header text color | `themes.xml` | `cometChatAIAssistantChatHistoryHeaderTextColor` | `?attr/cometchatTextColorPrimary` | +| Header text appearance | `themes.xml` | `cometChatAIAssistantChatHistoryHeaderTextAppearance` | `@style/textStyleTimesNewRoman` | +| New chat background color | `themes.xml` | `cometChatAIAssistantChatHistoryNewChatBackgroundColor` | `#FFFAF6` | +| New chat text color | `themes.xml` | `cometChatAIAssistantChatHistoryNewChatTextColor` | `?attr/cometchatTextColorPrimary` | +| New chat text appearance | `themes.xml` | `cometChatAIAssistantChatHistoryNewChatTextAppearance` | `@style/textStyleTimesNewRoman` | +| Date separator text appearance | `themes.xml` | `cometChatAIAssistantChatHistoryDateSeparatorTextAppearance` | `@style/textStyleTimesNewRoman` | +| Date separator text color | `themes.xml` | `cometChatAIAssistantChatHistoryDateSeparatorTextColor` | `?attr/cometchatTextColorTertiary` | +| Date separator background color | `themes.xml` | `cometChatAIAssistantChatHistoryDateSeparatorBackgroundColor` | `#FFFAF6` | +| Item background color | `themes.xml` | `cometChatAIAssistantChatHistoryItemBackgroundColor` | `#FFFAF6` | +| Item text appearance | `themes.xml` | `cometChatAIAssistantChatHistoryItemTextAppearance` | `@style/textStyleTimesNewRoman` | +| Item text color | `themes.xml` | `cometChatAIAssistantChatHistoryItemTextColor` | `?attr/cometchatTextColorPrimary` | +| Apply a custom style | Activity/Fragment | `setStyle(int styleRes)` | `binding.cometchatAiAssistantChatHistory.setStyle(R.style.CustomAIAssistantChatHistoryStyle);` | +| Set the user for fetching history | Activity/Fragment | `setUser(User)` | `.setUser(user);` | +| Error state visibility | Activity/Fragment | `setErrorStateVisibility(int)` | `.setErrorStateVisibility(View.GONE);` | +| Empty state visibility | Activity/Fragment | `setEmptyStateVisibility(int)` | `.setEmptyStateVisibility(View.GONE);` | + +## Next Steps + + + + Display messages in a conversation + + + Browse recent conversations + + + Browse and search available users + + + Search across conversations and messages + + \ No newline at end of file diff --git a/ui-kit/android/v6/ai-features.mdx b/ui-kit/android/v6/ai-features.mdx index 3a9ffa427..d9e7c149a 100644 --- a/ui-kit/android/v6/ai-features.mdx +++ b/ui-kit/android/v6/ai-features.mdx @@ -12,7 +12,7 @@ description: "Integrate AI-powered conversation starters, smart replies, and con | AI features | Conversation Starter, Smart Replies, Conversation Summary | | Key components | [Message List](/ui-kit/android/v6/message-list) (Conversation Starter), [Message Composer](/ui-kit/android/v6/message-composer) (Smart Replies, Summary) | | Activation | Enable each AI feature from the CometChat Dashboard — UI Kit auto-integrates them, no additional code required | -| Related | [Core Features](/ui-kit/android/v6/core-features), [Extensions](/ui-kit/android/v6/extensions), [AI Agent Guide](/ui-kit/android/guide-ai-agent) | +| Related | [Core Features](/ui-kit/android/v6/core-features), [Extensions](/ui-kit/android/v6/extensions), [AI Agent Guide](/ui-kit/android/v6/guide-ai-agent) | diff --git a/ui-kit/android/v6/call-features.mdx b/ui-kit/android/v6/call-features.mdx index 62041896a..55fa8540e 100644 --- a/ui-kit/android/v6/call-features.mdx +++ b/ui-kit/android/v6/call-features.mdx @@ -33,7 +33,7 @@ Add the following dependency to your `build.gradle.kts` file: ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta.1") + implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta2") implementation("com.cometchat:calls-sdk-android:4.3.3") } ``` @@ -42,7 +42,7 @@ dependencies { ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta.1") + implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta2") implementation("com.cometchat:calls-sdk-android:4.3.3") } ``` diff --git a/ui-kit/android/v6/calling-integration.mdx b/ui-kit/android/v6/calling-integration.mdx index d3c4902eb..73e6b10d9 100644 --- a/ui-kit/android/v6/calling-integration.mdx +++ b/ui-kit/android/v6/calling-integration.mdx @@ -19,7 +19,7 @@ Add the CometChat Calls SDK dependency alongside your chosen UI Kit module: ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta.1") + implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta2") implementation("com.cometchat:calls-sdk-android:4.3.3") } ``` @@ -28,7 +28,7 @@ dependencies { ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta.1") + implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta2") implementation("com.cometchat:calls-sdk-android:4.3.3") } ``` diff --git a/ui-kit/android/v6/component-styling.mdx b/ui-kit/android/v6/component-styling.mdx index c04771737..d1313cc3b 100644 --- a/ui-kit/android/v6/component-styling.mdx +++ b/ui-kit/android/v6/component-styling.mdx @@ -704,6 +704,181 @@ Attribute references: - [Mentions attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit-kotlin/src/main/res/values/attr_cometchat_mentions.xml) - [Message Bubble attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit-kotlin/src/main/res/values/attr_cometchat_message_bubble.xml) +#### Search + +The `CometChatSearch` component provides cross-conversation and message search UI. + + + + + +What you're changing: search background and typography styles. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatSearch` + +- **Default behavior**: UI Kit default search colors and text appearance. + +- **Override**: set `cometchatSearchStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + + + +``` + +- **What this does**: applies custom search colors and text styles across search UI sections. + +- **Verify**: open Search and check section headers, chips, and list items. + +Attribute references: +- [Search attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_search.xml) + + +#### Message Information + +The `CometChatMessageInformation` component displays message metadata such as delivery and read status. + + + + + +What you're changing: message information styling for metadata views. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatMessageInformation` + +- **Default behavior**: UI Kit default metadata styling. + +- **Override**: set `cometchatMessageInformationStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: wires a custom Message Information style so you can override specific metadata attributes. + +- **Verify**: open Message Information and confirm your overrides apply. + +Attribute references: +- [Message Information attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_message_information.xml) + + +#### Message Option Sheet + +The `CometChatMessageOptionSheet` component is the action menu for message-level actions. + + + + + +What you're changing: option sheet background and icon tint. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatMessageOptionSheet` + +- **Default behavior**: UI Kit default option sheet styling. + +- **Override**: set `cometchatPopupMenuStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: updates message option sheet icon tint and background color. + +- **Verify**: long-press a message and confirm the option sheet styling. + +Attribute references: +- [Message Option Sheet attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_message_option_sheet.xml) + + +#### Attachment Option Sheet + +The `CometChatAttachmentOptionSheet` component renders the attachment picker. + + + + + +What you're changing: attachment option sheet background and icon tint. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatAttachmentOptionSheet` + +- **Default behavior**: UI Kit default attachment sheet styling. + +- **Override**: set `cometchatAttachmentOptionSheetStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: applies custom colors to the attachment picker sheet. + +- **Verify**: open the attachment menu and confirm background and icons. + +Attribute references: +- [Attachment Option Sheet attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_attachment_option_sheet.xml) + + --- ### Calling UI @@ -879,6 +1054,272 @@ Attribute references: - [Outgoing Call attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit-kotlin/src/main/res/values/attr_cometchat_outgoing_call.xml) - [Avatar attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit-kotlin/src/main/res/values/attr_cometchat_avatar.xml) +#### Call Buttons + +The `CometChatCallButton` component renders voice and video call buttons. + + + + + +What you're changing: button background, stroke, and icon tint. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatCallButtons` + +- **Default behavior**: UI Kit default call button styling. + +- **Override**: set `cometchatCallButtonsStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: customizes call button padding, background, and icon colors. + +- **Verify**: open a chat header and confirm button styling. + +Attribute references: +- [Call Buttons attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_call_buttons.xml) + + +--- + +### AI UI +#### AI Assistant Chat History + +The `CometChatAIAssistantChatHistory` component renders the AI conversation history view. + + + + + +What you're changing: background, header, and list typography. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatAIAssistantChatHistory` + +- **Default behavior**: UI Kit default AI history styling. + +- **Override**: set `cometChatAIAssistantChatHistoryStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + + + +``` + +- **What this does**: applies custom colors and font styling to the AI Assistant history screen. + +- **Verify**: open AI Assistant history and confirm background and header styling. + +Attribute references: +- [AI Assistant Chat History attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_ai_assistant_chat_history.xml) + +#### AI Option Sheet + +The `CometChatAIOptionSheet` component renders AI action options. + + + + + +What you're changing: AI option sheet background and icon tint. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatAIOptionSheet` + +- **Default behavior**: UI Kit default AI option sheet styling. + +- **Override**: set `cometchatAIOptionSheetStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: customizes AI option sheet colors. + +- **Verify**: open AI actions and confirm the option sheet styling. + +Attribute references: +- [AI Option Sheet attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_ai_option_sheet.xml) + +#### Conversation Starter + +The `CometChatConversationStarter` component renders AI-powered conversation starters. + + + + + +What you're changing: conversation starter item backgrounds. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatConversationStarter` + +- **Default behavior**: UI Kit default conversation starter styling. + +- **Override**: set `cometchatAIConversationStarterStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: applies a custom background color to AI conversation starter items. + +- **Verify**: open a new chat and confirm the conversation starter chip color. + +Attribute references: +- [AI Conversation Starter attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_ai_conversation_starter.xml) + +#### Conversation Summary + +The `CometChatConversationSummary` component renders AI-generated summaries of chats. + + + + + +What you're changing: conversation summary background color. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatConversationSummary` + +- **Default behavior**: UI Kit default conversation summary styling. + +- **Override**: set `cometchatAIConversationSummaryStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: applies a custom background color to conversation summary cards. + +- **Verify**: open a chat summary and confirm the background color. + +Attribute references: +- [AI Conversation Summary attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_ai_conversation_summary.xml) + +#### Smart Replies + +The `CometChatSmartReplies` component renders AI-generated reply suggestions. + + + + + +What you're changing: smart reply background, item color, and stroke. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatSmartReplies` + +- **Default behavior**: UI Kit default smart replies styling. + +- **Override**: set `cometchatAISmartRepliesStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: customizes smart reply container and chip styling. + +- **Verify**: open a conversation with smart replies enabled and confirm chip styling. + +Attribute references: +- [AI Smart Replies attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_ai_smart_replies.xml) + + --- ### Base Components @@ -1092,6 +1533,184 @@ CometChatReactionList( Attribute references: - [Reaction List attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit-kotlin/src/main/res/values/attr_cometchat_reaction_list.xml) +#### Date + +The `CometChatDate` component formats timestamps in lists and message threads. + + + + + +What you're changing: date text color. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatDate` + +- **Default behavior**: UI Kit default date styling. + +- **Override**: set `cometchatDateStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: customizes date text color in UI Kit lists and headers. + +- **Verify**: check any timestamp and confirm the color. + +Attribute references: +- [Date attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_date.xml) + + +#### Receipts + +The `CometChatReceipts` component renders read and delivered status icons. + + + + + +What you're changing: read receipt icon drawable. + +- **Where to change it**: `res/drawable/read_receipts.xml` and `res/values/themes.xml` + +- **Applies to**: `CometChatReceipts` + +- **Default behavior**: UI Kit default receipt icons. + +- **Override**: set `cometchatMessageReceiptStyle` in `AppTheme` and reference a custom drawable. + +- **Code**: +```xml res/drawable/read_receipts.xml lines + + + + +``` + +- **What this does**: defines a custom read receipt icon. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: applies the custom receipt icon to message status indicators. + +- **Verify**: send a message and check the receipt icon for read status. + +Attribute references: +- [Message Receipt attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_message_receipt.xml) + + +#### Media Recorder + +The `CometChatMediaRecorder` component controls audio and video message recording. + + + + + +What you're changing: recorder icon sizes and recording button background color. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatMediaRecorder` + +- **Default behavior**: UI Kit default media recorder styling. + +- **Override**: set `cometchatMediaRecorderStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: applies custom sizing and color to the media recorder UI. + +- **Verify**: open the recorder and check icon sizes and record button color. + +Attribute references: +- [Media Recorder attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_media_recorder.xml) + + +#### Sticker Keyboard + +The `CometChatStickerKeyboard` component renders the sticker picker UI. + + + + + +What you're changing: sticker keyboard background color. + +- **Where to change it**: `res/values/themes.xml` + +- **Applies to**: `CometChatStickerKeyboard` + +- **Default behavior**: UI Kit default sticker keyboard styling. + +- **Override**: set `cometchatStickerKeyboardStyle` in `AppTheme`. + +- **Code**: +```xml res/values/themes.xml lines + + + + + +``` + +- **What this does**: applies a custom background color to the sticker keyboard. + +- **Verify**: open the sticker keyboard and confirm the background color. + +Attribute references: +- [Sticker Keyboard attributes](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_sticker_keyboard.xml) + + --- ## Customization matrix @@ -1103,5 +1722,10 @@ Attribute references: | Group list titles | `themes.xml` / style param | `cometchatGroupsStyle` | `cometchatGroupsTitleTextColor` | | Header call icons | `themes.xml` / style param | `cometchatMessageHeaderStyle` | `cometchatMessageHeaderCallButtonsStyle` | | Message list background | `themes.xml` / style param | `cometchatMessageListStyle` | `cometchatMessageListBackgroundColor` | +| Search styling | `themes.xml` / style param | `cometchatSearchStyle` | `cometchatSearchBackgroundColor` | +| Message Information | `themes.xml` / style param | `cometchatMessageInformationStyle` | `cometchatMessageInformationBackgroundColor` | +| AI Chat History | `themes.xml` / style param | `cometchatAIAssistantChatHistoryStyle` | `cometchatAIAssistantChatHistoryBackgroundColor` | +| Date formatting | `themes.xml` / style param | `cometchatDateStyle` | `cometchatDateTextColor` | +| Receipts icons | `themes.xml` / style param | `cometchatReceiptsStyle` | `cometchatReceiptsReadColor` | | Composer send button | `res/drawable/` + `themes.xml` | `cometchatMessageComposerActiveSendButtonDrawable` | `@drawable/active_send_button` | | Call buttons styling | `themes.xml` / style param | `cometchatCallButtonsStyle` | `cometchatCallButtonsVideoCallBackgroundColor` | diff --git a/ui-kit/android/v6/core-features.mdx b/ui-kit/android/v6/core-features.mdx index fcc6bef02..ad33a43b5 100644 --- a/ui-kit/android/v6/core-features.mdx +++ b/ui-kit/android/v6/core-features.mdx @@ -9,7 +9,7 @@ description: "Overview of CometChat's core chat features including instant messa | --- | --- | | Packages | `com.cometchat:chatuikit-kotlin-android` (Kotlin XML Views), `com.cometchat:chatuikit-compose-android` (Jetpack Compose) | | Required setup | `CometChatUIKit.init()` then `CometChatUIKit.login()` — must complete before rendering any component | -| Core features | Instant Messaging, Media Sharing, Read Receipts, Mark as Unread, Typing Indicator, User Presence, Reactions, Mentions, Quoted Reply, Threaded Conversations, Moderation, Report Message, Group Chat | +| Core features | Instant Messaging, Media Sharing, Read Receipts, Mark as Unread, Typing Indicator, User Presence, Reactions, Mentions, Rich Text Formatting, Quoted Reply, Search, Threaded Conversations, Moderation, Report Message, Group Chat | | Key components | `CometChatConversations`, `CometChatMessageList`, `CometChatMessageComposer`, `CometChatMessageHeader`, `CometChatUsers`, `CometChatGroups`, `CometChatGroupMembers`, `CometChatMessageInformation`, `CometChatThreadHeader` | | Theming | See [Theming](/ui-kit/android/v6/theme-introduction) | @@ -124,6 +124,19 @@ Address specific users in a conversation by typing `@` to trigger mention sugges | [CometChatMessageComposer](/ui-kit/android/v6/message-composer) | Triggers mention suggestions on `@` and inserts formatted mentions. | | [CometChatMessageList](/ui-kit/android/v6/message-list) | Renders mentions with distinct styling in the message flow. | + +## Rich Text Formatting + +Rich Text Formatting allows users to style their messages with bold, italic, strikethrough, code, code blocks, blockquotes, ordered/unordered lists, and links. This brings richer expression to conversations and helps users emphasize key points. + + + + + +| Component | Role | +| --- | --- | +| [CometChatMessageComposer](/ui-kit/android/v6/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/v6/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. @@ -196,6 +209,21 @@ Learn more about flagged messages in the [Flagged Messages](/moderation/flagged- | --- | --- | | [CometChatMessageList](/ui-kit/android/v6/message-list) | Provides the "Report Message" option in message actions. | +## Conversation and Advanced Search + +Conversation and Advanced Search enables users to quickly find conversations, messages, and media across chats in real time. It supports filters, scopes, and custom actions, allowing users to locate content efficiently while keeping the chat experience smooth and intuitive. + + + + + +| Component | Role | +| --- | --- | +| [CometChatSearch](/ui-kit/android/v6/search) | Allows users to search across conversations and messages in real time. Users can click on a result to open the conversation or jump directly to a specific message. | +| [CometChatMessageHeader](/ui-kit/android/v6/message-header) | Shows the search button in the chat header, allowing users to search within a conversation. | +| [CometChatMessageList](/ui-kit/android/v6/message-list) | Shows the selected message when clicked from search results and highlights it in the message list. | +| [CometChatConversations](/ui-kit/android/v6/conversations) | Displays the search input. | + --- ## Next Steps diff --git a/ui-kit/android/v6/getting-started-jetpack.mdx b/ui-kit/android/v6/getting-started-jetpack.mdx index 0c28eb164..70daf0f25 100644 --- a/ui-kit/android/v6/getting-started-jetpack.mdx +++ b/ui-kit/android/v6/getting-started-jetpack.mdx @@ -98,7 +98,7 @@ android { dependencies { // CometChat Jetpack Compose UI Kit - implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta.1") + implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta2") // (Optional) Voice/video calling implementation("com.cometchat:calls-sdk-android:latest") diff --git a/ui-kit/android/v6/getting-started-kotlin.mdx b/ui-kit/android/v6/getting-started-kotlin.mdx index 2170d4953..f11460459 100644 --- a/ui-kit/android/v6/getting-started-kotlin.mdx +++ b/ui-kit/android/v6/getting-started-kotlin.mdx @@ -93,7 +93,7 @@ android { dependencies { // CometChat Kotlin UI Kit - implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta.1") + implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta2") // (Optional) Voice/video calling implementation("com.cometchat:calls-sdk-android:latest") diff --git a/ui-kit/android/v6/getting-started.mdx b/ui-kit/android/v6/getting-started.mdx index e44f68347..af5b47393 100644 --- a/ui-kit/android/v6/getting-started.mdx +++ b/ui-kit/android/v6/getting-started.mdx @@ -88,7 +88,7 @@ Inside `libs.versions.toml`, add the versions: ```toml libs.versions.toml [versions] -cometchat-ui-kit = "6.0.0-beta.1" +cometchat-ui-kit = "6.0.0-beta2" cometchat-calls-sdk = "4.3.3" ``` @@ -132,7 +132,7 @@ Open the app-level `build.gradle.kts` file and add the dependency for your chose ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta.1") + implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta2") // (Optional) Include if using voice/video calling features implementation("com.cometchat:calls-sdk-android:4.3.3") @@ -143,7 +143,7 @@ dependencies { ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta.1") + implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta2") // (Optional) Include if using voice/video calling features implementation("com.cometchat:calls-sdk-android:4.3.3") diff --git a/ui-kit/android/v6/guide-ai-agent.mdx b/ui-kit/android/v6/guide-ai-agent.mdx new file mode 100644 index 000000000..2da496105 --- /dev/null +++ b/ui-kit/android/v6/guide-ai-agent.mdx @@ -0,0 +1,419 @@ +--- +title: "AI Agent Integration" +sidebarTitle: "AI Agent Integration" +description: "Enable AI-powered conversational assistance with chat history, contextual responses, and seamless handoffs." +--- + + + +| Field | Value | +| --- | --- | +| Packages | `com.cometchat:chatuikit-kotlin-android` · `com.cometchat:chatuikit-compose-android` | +| Key components | `CometChatAIAssistantChatHistory`, `CometChatMessageList`, `CometChatMessageComposer`, `CometChatMessageHeader` | +| Purpose | Enable AI-powered conversational assistance with chat history, contextual responses, and seamless handoffs. | +| Related | [AI Assistant Chat History](/ui-kit/android/v6/ai-assistant-chat-history), [AI Features](/ui-kit/android/v6/ai-features), [All Guides](/ui-kit/android/v6/guide-overview) | + + + +Enable intelligent conversational AI capabilities in your Android app using CometChat UIKit v6 with AI Agent integration: + +- **AI Assistant Chat History** +- **Chat History Management** +- **Contextual Responses** +- **Agent Detection** +- **Seamless Handoffs** + +Transform your chat experience with AI-powered assistance that provides intelligent responses and seamless integration with your existing chat infrastructure. + +## Overview + +Users can interact with AI agents through a dedicated chat interface that: + +- Provides intelligent responses based on conversation context. +- Maintains chat history for continuity. +- Seamlessly integrates with your existing user chat system. + +The AI Agent chat interface provides a familiar messaging experience enhanced with AI capabilities, accessible through your main chat flow or as a standalone feature. + + + + + +## Prerequisites + +- Android Studio project with `com.cometchat:chatuikit-kotlin-android` or `com.cometchat:chatuikit-compose-android` in `build.gradle`. +- Internet permission in `AndroidManifest.xml`. +- Valid CometChat **App ID**, **Region**, and **Auth Key** configured via `UIKitSettings`. +- User logged in with `CometChatUIKit.login()`. +- AI Agent configured in your CometChat dashboard. + +## Components + +| Component / Class | Role | +|:----------------------------------|:-----| +| `AIAssistantChatActivity` | Main activity for AI agent chat. | +| `CometChatAIAssistantChatHistory` | Displays previous AI conversation history. | +| `CometChatMessageList` | Shows AI messages with threading support. | +| `CometChatMessageComposer` | Input interface for AI conversations. | +| `CometChatMessageHeader` | Header with AI agent info and controls. | + +## Integration Steps + +### Step 1 — Activity / Screen Setup + +Create the AI Assistant chat screen with proper layout configuration. + + + +```kotlin AIAssistantChatActivity.kt lines +class AIAssistantChatActivity : AppCompatActivity() { + private lateinit var binding: ActivityAiAssistantChatBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityAiAssistantChatBinding.inflate(layoutInflater) + setContentView(binding.root) + + val messageJson = intent.getStringExtra(getString(R.string.app_base_message)) + val userJson = intent.getStringExtra(getString(R.string.app_user)) + + var user: User? = null + var parentMessage: BaseMessage? = null + + if (!userJson.isNullOrEmpty()) + user = User.fromJson(userJson) + if (!messageJson.isNullOrEmpty()) + parentMessage = BaseMessage.processMessage(JSONObject(messageJson)) + + initializeComponents(user, parentMessage) + initClickListeners() + } + + private fun initializeComponents(user: User?, parentMessage: BaseMessage?) { + user?.let { + binding.messageHeader.user = it + binding.messageList.user = it + binding.messageComposer.user = it + } + + if (parentMessage != null) { + binding.messageList.setParentMessage(parentMessage.getId()) + binding.messageComposer.setParentMessageId(parentMessage.getId()) + } + + binding.messageList.setStyle(R.style.CustomCometChatMessageListStyle) + binding.messageComposer.style = R.style.CustomMessageComposerStyle + } +} +``` + + + +```kotlin AIAssistantChatScreen.kt lines +import com.cometchat.uikit.compose.presentation.messageheader.ui.CometChatMessageHeader +import com.cometchat.uikit.compose.presentation.messagelist.ui.CometChatMessageList +import com.cometchat.uikit.compose.presentation.messagecomposer.ui.CometChatMessageComposer + +@Composable +fun AIAssistantChatScreen( + user: User, + parentMessageId: Int? = null, + onNewChatClick: () -> Unit = {}, + onChatHistoryClick: () -> Unit = {} +) { + Column(modifier = Modifier.fillMaxSize()) { + CometChatMessageHeader( + user = user + ) + + CometChatMessageList( + user = user, + parentMessageId = parentMessageId ?: 0, + modifier = Modifier.weight(1f) + ) + + CometChatMessageComposer( + user = user, + parentMessageId = parentMessageId ?: 0 + ) + } +} +``` + + + +**File reference:** +[`AIAssistantChatActivity.kt`](https://github.com/cometchat/cometchat-uikit-android/blob/v6/ai-sample-app/src/main/java/com/cometchat/ai/sampleapp/ui/activity/AIAssistantChatActivity.kt) + +### Step 2 — Layout (XML Views only) + +Add `CometChatMessageHeader`, `CometChatMessageList`, and `CometChatMessageComposer` to your layout. + +```xml activity_ai_assistant_chat.xml lines + + + + + + + + + +``` + +> **Note:** In Jetpack Compose, layout is handled declaratively in the composable function — no XML needed. + +### Step 3 — Style of Message List & Composer (XML Views only) + +Define custom styles for the message list and composer to differentiate AI agent chats. + +```xml themes.xml lines + + + + + +``` + +> **Jetpack Compose:** Pass a custom style object via the `style` parameter on each composable instead of XML styles. + +### Step 4 — Initialize click listeners + +Initialize click listeners to handle new chat creation and chat history access. + + + +```kotlin AIAssistantChatActivity.kt lines +private fun initClickListeners() { + // New chat creation + binding.messageHeader.setNewChatButtonClick { + Utils.hideKeyBoard(this@AIAssistantChatActivity, binding.root) + val intent = Intent(this@AIAssistantChatActivity, AIAssistantChatActivity::class.java) + intent.putExtra(getString(R.string.app_user), user.toJson().toString()) + startActivity(intent) + finish() + } + + // Chat history access + binding.messageHeader.setChatHistoryButtonClick { + val intent = Intent(this@AIAssistantChatActivity, AIAssistantChatHistoryActivity::class.java) + intent.putExtra(getString(R.string.app_user), user.toJson().toString()) + startActivity(intent) + } +} +``` + + + +```kotlin AIAssistantChatScreen.kt lines +// In Jetpack Compose, pass callbacks as lambda parameters: +AIAssistantChatScreen( + user = aiUser, + onNewChatClick = { + // Navigate to a fresh AI chat screen + navController.navigate("ai_chat/${Gson().toJson(aiUser)}") + }, + onChatHistoryClick = { + // Navigate to chat history screen + navController.navigate("ai_chat_history/${Gson().toJson(aiUser)}") + } +) +``` + + + +### Step 5 — AI Assistant Chat History screen + +Create a screen to host the `CometChatAIAssistantChatHistory` component. + + + +```kotlin AIAssistantChatHistoryActivity.kt lines +class AIAssistantChatHistoryActivity : AppCompatActivity() { + private lateinit var binding: ActivityAiAssistantChatHistoryBinding + private var user: User? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityAiAssistantChatHistoryBinding.inflate(layoutInflater) + setContentView(binding.root) + + val userJson = intent.getStringExtra(getString(R.string.app_user)) + + if (userJson != null && userJson.isNotEmpty()) { + user = User.fromJson(userJson) + binding.cometchatAiAssistantChatHistory.setUser(user) + } + + initClickListeners() + } + + private fun initClickListeners() { + binding.cometchatAiAssistantChatHistory.setOnItemClickListener { view, position, message -> + val appEntity = message.getReceiver() + if (appEntity is User) { + user = appEntity + val intent = Intent(this, AIAssistantChatActivity::class.java) + intent.putExtra(getString(R.string.app_user), appEntity.toJson().toString()) + intent.putExtra(getString(R.string.app_base_message), message.getRawMessage().toString()) + startActivity(intent) + finish() + } + } + + binding.cometchatAiAssistantChatHistory.setOnNewChatClickListener { + val intent = Intent(this, AIAssistantChatActivity::class.java) + intent.putExtra(getString(R.string.app_user), user!!.toJson().toString()) + startActivity(intent) + finish() + } + + binding.cometchatAiAssistantChatHistory.setOnCloseClickListener { + finish() + } + } +} +``` + + + +```kotlin AIAssistantChatHistoryScreen.kt lines +import com.cometchat.uikit.compose.presentation.aiassistantchathistory.ui.CometChatAIAssistantChatHistory + +@Composable +fun AIAssistantChatHistoryScreen( + user: User, + navController: NavController +) { + CometChatAIAssistantChatHistory( + modifier = Modifier.fillMaxSize(), + onCloseClick = { + navController.popBackStack() + }, + onNewChatClick = { + navController.navigate("ai_chat/${Gson().toJson(user)}") + }, + onItemClick = { message -> + val receiver = message.receiver + if (receiver is User) { + navController.navigate( + "ai_chat/${Gson().toJson(receiver)}/${message.rawMessage}" + ) + } + } + ) +} +``` + + + +### Step 6 — Chat History layout (XML Views only) + +```xml activity_ai_assistant_chat_history.xml lines + + + + + +``` + +> **Note:** In Jetpack Compose, the `CometChatAIAssistantChatHistory` composable is used directly — no XML layout needed. + +### Step 7 — Launching AI Chat + + + +```kotlin lines +fun launchAIAssistantChat(aiUser: User) { + val intent = Intent(this, AIAssistantChatActivity::class.java) + intent.putExtra(getString(R.string.app_user), aiUser.toJson().toString()) + startActivity(intent) +} +``` + + + +```kotlin lines +// In your NavHost setup: +composable("ai_chat/{userJson}") { backStackEntry -> + val userJson = backStackEntry.arguments?.getString("userJson") + val user = Gson().fromJson(userJson, User::class.java) + AIAssistantChatScreen(user = user) +} + +composable("ai_chat_history/{userJson}") { backStackEntry -> + val userJson = backStackEntry.arguments?.getString("userJson") + val user = Gson().fromJson(userJson, User::class.java) + AIAssistantChatHistoryScreen(user = user, navController = navController) +} +``` + + + +## Implementation Flow Summary + +| Step | Action | +|:-----|:-------| +| 1 | User selects AI agent from chat list | +| 2 | AI chat screen launches | +| 3 | Parse intent data and detect agent chat (Role of user must be "@agentic") | +| 4 | Initialize UI with AI-specific styling | +| 5 | Configure chat history and navigation | +| 6 | Launch chat with AI agent | + +## Customization Options + +- **Custom AI Assistant Empty Chat View:** Customize the empty state view using `setAIAssistantEmptyChatGreetingView()` (XML Views) or the `emptyView` composable slot (Compose). +- **Streaming Speed:** Adjust AI response streaming speed via `setStreamingSpeed()`. +- **AI Assistant Suggested Messages:** Create custom list of suggested messages using `setAIAssistantSuggestedMessages()`. +- **AI Assistant Tools:** Set tools for the AI agent using `setAIAssistantTools()`. + +## Feature Matrix + +| Feature | Kotlin (XML Views) | Jetpack Compose | +|:--------|:-------------------|:----------------| +| AI Chat Interface | `AIAssistantChatActivity` | `AIAssistantChatScreen` composable | +| Chat History | `CometChatAIAssistantChatHistory` XML | `CometChatAIAssistantChatHistory()` composable | +| Message List | `CometChatMessageList` XML | `CometChatMessageList()` composable | +| Message Composer | `CometChatMessageComposer` XML | `CometChatMessageComposer()` composable | + + + + Explore this feature in the CometChat AI Builder: + [GitHub → AI Builder](https://github.com/cometchat/cometchat-uikit-android/tree/v6/ai-sample-app) + + + Explore this feature in the CometChat SampleApp: + [GitHub → SampleApp](https://github.com/cometchat/cometchat-uikit-android/tree/v6/sample-app-kotlin) + + diff --git a/ui-kit/android/v6/guide-overview.mdx b/ui-kit/android/v6/guide-overview.mdx index 21ec20682..2551ff7d3 100644 --- a/ui-kit/android/v6/guide-overview.mdx +++ b/ui-kit/android/v6/guide-overview.mdx @@ -12,7 +12,7 @@ description: "Index of task-oriented feature guides for the CometChat Android UI | Purpose | Index of task-oriented feature guides for the Android UI Kit | | Sample app | [GitHub](https://github.com/cometchat/cometchat-uikit-android/tree/v6/sample-app-kotlin) | | Components | [Components Overview](/ui-kit/android/v6/components-overview) | -| Guides | [Block/Unblock](/ui-kit/android/v6/guide-block-unblock-user) · [Call Log Details](/ui-kit/android/v6/guide-call-log-details) · [Group Chat](/ui-kit/android/v6/guide-group-chat) · [Message Privately](/ui-kit/android/v6/guide-message-privately) · [New Chat](/ui-kit/android/v6/guide-new-chat) · [Search Messages](/ui-kit/android/guide-search-messages) · [Threaded Messages](/ui-kit/android/v6/guide-threaded-messages) · [AI Agent](/ui-kit/android/guide-ai-agent) · [Custom Text Formatter](/ui-kit/android/v6/custom-text-formatter-guide) · [Mentions Formatter](/ui-kit/android/v6/mentions-formatter-guide) · [ShortCut Formatter](/ui-kit/android/v6/shortcut-formatter-guide) | +| Guides | [Block/Unblock](/ui-kit/android/v6/guide-block-unblock-user) · [Call Log Details](/ui-kit/android/v6/guide-call-log-details) · [Group Chat](/ui-kit/android/v6/guide-group-chat) · [Message Privately](/ui-kit/android/v6/guide-message-privately) · [New Chat](/ui-kit/android/v6/guide-new-chat) · [Search Messages](/ui-kit/android/v6/guide-search-messages) · [Threaded Messages](/ui-kit/android/v6/guide-threaded-messages) · [AI Agent](/ui-kit/android/v6/guide-ai-agent) · [Custom Text Formatter](/ui-kit/android/v6/custom-text-formatter-guide) · [Mentions Formatter](/ui-kit/android/v6/mentions-formatter-guide) · [ShortCut Formatter](/ui-kit/android/v6/shortcut-formatter-guide) | @@ -39,8 +39,8 @@ The CometChat Android UI Kit is available in two modules: | [Message Privately](/ui-kit/android/v6/guide-message-privately) | Launch a direct 1:1 chat from a profile/list; optionally send initial message to surface conversation. | | [New Chat](/ui-kit/android/v6/guide-new-chat) | Unified entry for starting new 1:1 or group chats with tabbed Users / Groups search + navigation. | | [Threaded Messages](/ui-kit/android/v6/guide-threaded-messages) | Threaded replies: open parent message context, list replies, compose with parent linkage. | -| [Search Messages](/ui-kit/android/guide-search-messages) | Full-text message search across conversations with result routing and navigation. | -| [AI Agent](/ui-kit/android/guide-ai-agent) | Build an AI-powered agent that responds to user messages using CometChat's AI features. | +| [Search Messages](/ui-kit/android/v6/guide-search-messages) | Full-text message search across conversations with result routing and navigation. | +| [AI Agent](/ui-kit/android/v6/guide-ai-agent) | Build an AI-powered agent that responds to user messages using CometChat's AI features. | | [Custom Text Formatter](/ui-kit/android/v6/custom-text-formatter-guide) | Extend `CometChatTextFormatter` to build custom inline text patterns with tracking characters and suggestion lists. | | [Mentions Formatter](/ui-kit/android/v6/mentions-formatter-guide) | Format @mentions with styled tokens, suggestion lists, and click handling. | | [ShortCut Formatter](/ui-kit/android/v6/shortcut-formatter-guide) | Add shortcut text expansion to the message composer via the message-shortcuts extension. | diff --git a/ui-kit/android/v6/guide-search-messages.mdx b/ui-kit/android/v6/guide-search-messages.mdx new file mode 100644 index 000000000..d533d4d82 --- /dev/null +++ b/ui-kit/android/v6/guide-search-messages.mdx @@ -0,0 +1,235 @@ +--- +title: "Search Messages" +sidebarTitle: "Search Messages" +description: "Add full-text message search across conversations with result routing in CometChat Android UI Kit." +--- + + + +| Field | Value | +| --- | --- | +| Packages | `com.cometchat:chatuikit-kotlin-android` · `com.cometchat:chatuikit-compose-android` | +| Key components | `CometChatSearch`, `CometChatMessageList`, `CometChatMessageComposer`, `CometChatMessageHeader` | +| Purpose | Full-text message search across conversations with result routing and navigation | +| Sample app | [GitHub](https://github.com/cometchat/cometchat-uikit-android/tree/v6/sample-app-kotlin) | +| Related | [Search Component](/ui-kit/android/v6/search), [All Guides](/ui-kit/android/v6/guide-overview) | + + + +Search Messages lets users locate specific messages across conversations and groups using keyword search, then navigate directly to the result. + +Before starting, complete the [Getting Started](/ui-kit/android/v6/getting-started) guide. + +--- + +## Components + +| Component / Class | Role | +|:---|:---| +| `CometChatSearch` | Main search interface with filter chips and result lists | +| `CometChatMessageList` | Displays messages in the selected conversation | +| `CometChatMessageComposer` | Message input for the selected conversation | +| `CometChatMessageHeader` | Header bar showing conversation context | + +--- + +## Integration Steps + +### 1. Add CometChatSearch to your layout + + + +```xml activity_search.xml lines + +``` + + + +```kotlin SearchScreen.kt lines +import com.cometchat.uikit.compose.presentation.search.ui.CometChatSearch + +@Composable +fun SearchScreen(navController: NavController) { + CometChatSearch( + modifier = Modifier.fillMaxSize(), + onConversationClick = { conversation -> + navController.navigate("messages/${Gson().toJson(conversation)}") + }, + onMessageClick = { message -> + if (message.receiverType == CometChatConstants.RECEIVER_TYPE_USER) { + navController.navigate("messages/user/${(message.receiver as User).uid}/${message.id}") + } else { + navController.navigate("messages/group/${(message.receiver as Group).guid}/${message.id}") + } + } + ) +} +``` + + + +--- + +### 2. Handle conversation result clicks + +When a user taps a conversation result, navigate to the message view for that conversation. + + + +```kotlin SearchActivity.kt lines +val search: CometChatSearch = findViewById(R.id.cometchat_search) + +search.setOnConversationClicked { view, position, conversation -> + val intent = Intent(this, ChatActivity::class.java) + intent.putExtra("conversation", conversation) + startActivity(intent) +} +``` + + + +```kotlin SearchScreen.kt lines +// Handled inline via onConversationClick lambda in the CometChatSearch composable above +``` + + + +--- + +### 3. Handle message result clicks + +When a user taps a message result, navigate to the conversation and scroll to that message. + + + +```kotlin SearchActivity.kt lines +search.setOnMessageClicked { view, position, message -> + val intent = Intent(this, ChatActivity::class.java) + if (message.receiverType == CometChatConstants.RECEIVER_TYPE_USER) { + intent.putExtra("uid", (message.receiver as User).uid) + } else { + intent.putExtra("guid", (message.receiver as Group).guid) + } + intent.putExtra("messageId", message.id) + startActivity(intent) +} +``` + + + +```kotlin SearchScreen.kt lines +// Handled inline via onMessageClick lambda in the CometChatSearch composable above +``` + + + +--- + +### 4. Scope search to a specific conversation (optional) + +Restrict search results to a single user or group conversation. + + + +```kotlin lines +// Scope to a specific user conversation +search.setUid("alice-uid") + +// Or scope to a specific group conversation +search.setGuid("group-guid") +``` + + + +```kotlin lines +// Scope to a specific user conversation +CometChatSearch( + uid = "alice-uid", + modifier = Modifier.fillMaxSize(), + onConversationClick = { /* handle click */ }, + onMessageClick = { /* handle click */ } +) + +// Or scope to a specific group conversation +CometChatSearch( + guid = "group-guid", + modifier = Modifier.fillMaxSize(), + onConversationClick = { /* handle click */ }, + onMessageClick = { /* handle click */ } +) +``` + + + +--- + +### 5. Configure search filters (optional) + +Control which filter chips appear. + + + +```kotlin lines +search.setSearchFilters( + listOf( + UIKitConstants.SearchFilter.MESSAGES, + UIKitConstants.SearchFilter.CONVERSATIONS + ) +) +search.setInitialSearchFilter(UIKitConstants.SearchFilter.MESSAGES) +``` + + + +```kotlin lines +import com.cometchat.uikit.core.constants.SearchFilter + +CometChatSearch( + searchFilters = listOf( + SearchFilter.UNREAD, + SearchFilter.GROUPS, + SearchFilter.PHOTOS, + SearchFilter.VIDEOS + ), + modifier = Modifier.fillMaxSize(), + onConversationClick = { /* handle click */ }, + onMessageClick = { /* handle click */ } +) +``` + + + +--- + +## Feature Matrix + +| Feature | Kotlin (XML Views) | Jetpack Compose | +|:---|:---|:---| +| Search interface | `CometChatSearch` XML element | `CometChatSearch()` composable | +| Conversation results | `setOnConversationClicked` | `onConversationClick` lambda | +| Message results | `setOnMessageClicked` | `onMessageClick` lambda | +| Scoped search | `setUid()` / `setGuid()` | `uid` / `guid` parameters | +| Filter chips | `setSearchFilters()` | `searchFilters` parameter | +| Custom views | `setEmptyView()`, `setErrorView()` | `emptyView`, `errorView` composable slots | + +--- + +## Next Steps + + + + The search component reference + + + Display messages in a conversation + + + Browse all feature and formatter guides + + + Full working sample application on GitHub + + diff --git a/ui-kit/android/v6/search.mdx b/ui-kit/android/v6/search.mdx new file mode 100644 index 000000000..7aa3cd0f2 --- /dev/null +++ b/ui-kit/android/v6/search.mdx @@ -0,0 +1,1136 @@ +--- +title: "Search" +description: "Real-time search interface for finding conversations and messages with filters, scopes, and customization options." +--- + + +```json +{ + "component": "CometChatSearch", + "package": "com.cometchat.uikit.kotlin.presentation.search (XML Views) / com.cometchat.uikit.compose.presentation.search (Compose)", + "xmlElement": "", + "description": "Real-time search interface for finding conversations and messages with filters, scopes, and customization options.", + "primaryOutput": { + "conversationClicked": { + "method": "setOnConversationClicked", + "type": "OnItemClick" + }, + "messageClicked": { + "method": "setOnMessageClicked", + "type": "OnItemClick" + } + }, + "methods": { + "data": { + "setConversationsRequestBuilder": { + "type": "ConversationsRequest.ConversationsRequestBuilder", + "default": "SDK default", + "note": "Pass the builder, not the result of .build()" + }, + "setMessagesRequestBuilder": { + "type": "MessagesRequest.MessagesRequestBuilder", + "default": "SDK default", + "note": "Pass the builder, not the result of .build()" + } + }, + "callbacks": { + "setOnConversationClicked": "OnItemClick", + "setOnMessageClicked": "OnItemClick", + "setOnBackPressListener": "OnBackPress", + "setOnError": "OnError", + "setOnEmpty": "OnEmpty", + "setOnLoadMessages": "OnLoad", + "setOnLoadConversations": "OnLoad" + }, + "visibility": { + "setEmptyStateVisibility": { "type": "int (View.VISIBLE | View.GONE)", "default": "View.VISIBLE" }, + "setErrorStateVisibility": { "type": "int", "default": "View.VISIBLE" } + }, + "functionality": { + "setUid": { "type": "String", "note": "Scope search to a specific user conversation" }, + "setGuid": { "type": "String", "note": "Scope search to a specific group conversation" }, + "setSearchFilters": { "type": "List", "note": "Filters rendered as chips" }, + "setInitialSearchFilter": { "type": "UIKitConstants.SearchFilter", "note": "Default active filter on load" }, + "setSearchIn": { "type": "List", "note": "Entities to search in" }, + "setHideGroupType": { "type": "boolean", "default": "false" }, + "setHideUserStatus": { "type": "boolean", "default": "false" } + }, + "viewSlots": { + "setConversationItemView": "ConversationsSearchViewHolderListener — entire conversation item row", + "setTextMessageItemView": "MessagesSearchViewHolderListener — text message item", + "setImageMessageItemView": "MessagesSearchViewHolderListener — image message item", + "setAudioMessageItemView": "MessagesSearchViewHolderListener — audio message item", + "setVideoMessageItemView": "MessagesSearchViewHolderListener — video message item", + "setDocumentMessageItemView": "MessagesSearchViewHolderListener — document message item", + "setLinkMessageItemView": "MessagesSearchViewHolderListener — link message item", + "setInitialView": "@LayoutRes int — initial state before search", + "setEmptyView": "@LayoutRes int — empty state", + "setLoadingView": "View — loading state", + "setErrorView": "View — error state" + }, + "advanced": { + "setTextFormatters": "List — custom text formatters", + "setDateTimeFormatter": "DateTimeFormatterCallback — custom date/time formatting", + "setMentionAllLabelId": "String id, String mentionAllLabel — custom mention-all label", + "setHintText": "String — search bar hint text" + }, + "style": { + "setStyle": { + "type": "@StyleRes int", + "parent": "CometChatSearchStyle" + } + } + }, + "events": [], + "sdkListeners": [] +} +``` + + + +## Where It Fits + +`CometChatSearch` is a composite search component. It searches across conversations and messages in real time and emits the selected result via `setOnConversationClicked` or `setOnMessageClicked`. Wire it to navigation so tapping a conversation opens the message view, or tapping a message scrolls to it in context. + + + +```kotlin ChatActivity.kt lines +class ChatActivity : AppCompatActivity() { + + private lateinit var search: CometChatSearch + private lateinit var messageHeader: CometChatMessageHeader + private lateinit var messageList: CometChatMessageList + private lateinit var messageComposer: CometChatMessageComposer + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_chat) + + search = findViewById(R.id.cometchat_search) + messageHeader = findViewById(R.id.message_header) + messageList = findViewById(R.id.message_list) + messageComposer = findViewById(R.id.message_composer) + + search.setOnConversationClicked { view, position, conversation -> + val user = conversation.conversationWith as? User + user?.let { + messageHeader.setUser(it) + messageList.setUser(it) + messageComposer.setUser(it) + } + } + + search.setOnMessageClicked { view, position, baseMessage -> + // Navigate to the message in context + } + } +} +``` + + +```kotlin lines +CometChatSearch( + onMessageClick = { message -> + Log.i(TAG, "Message Clicked!") + } +) +``` + + + +## Quick Start + +Add the component to your layout XML: + +```xml activity_search.xml lines + + + + + + +``` + + + + + +Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()`, a user logged in, and the `cometchat-chat-uikit-android` dependency added. + +To add programmatically in an Activity: + + + +```kotlin SearchActivity.kt lines +class SearchActivity : AppCompatActivity() { + private lateinit var binding: ActivitySearchBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySearchBinding.inflate(layoutInflater) + setContentView(binding.root) + } +} +``` + + +```kotlin lines +CometChatSearch( + onMessageClick = { message -> + Log.i(TAG, "Message Clicked!") + } +) +``` + + + +## Actions and Events + +### Callback Methods + +#### `setOnConversationClicked` + +Fires when a user taps a conversation from the search results. Primary navigation hook — set the active conversation and render the message view. + + + +```kotlin SearchActivity.kt lines +binding.cometchatSearch.setOnConversationClicked { view, position, conversation -> + Log.i(TAG, "onCreate: Conversation Clicked !") +} +``` + + +```kotlin lines +CometChatSearch( + onConversationClick = { conversation -> + Log.i(TAG, "Conversation Clicked!") + } +) +``` + + + +> **What this does:** Replaces the default conversation-click behavior. When a user taps a conversation item, your custom lambda executes. The callback receives the view, position, and `Conversation` object. + +#### `setOnMessageClicked` + +Fires when a user taps a message from the search results. Use to navigate to the message in context. + + + +```kotlin SearchActivity.kt lines +binding.cometchatSearch.setOnMessageClicked { view, position, baseMessage -> + Log.i(TAG, "onCreate: Message Clicked !") +} +``` + + +```kotlin lines +CometChatSearch( + onMessageClick = { message -> + Log.i(TAG, "Message Clicked!") + } +) +``` + + + +> **What this does:** Registers a callback that fires when the user taps a message in the search results. The callback receives the view, position, and `BaseMessage` object. + +#### `setOnBackPressListener` + +Fires when the user presses the back button in the search bar. Default: no predefined behavior. + + + +```kotlin SearchActivity.kt lines +binding.cometchatSearch.setOnBackPressListener { + Log.i(TAG, "onCreate: Back Pressed !") +} +``` + + +```kotlin lines +CometChatSearch( + onBackPress = { + Log.i(TAG, "Back Pressed!") + } +) +``` + + + +#### `setOnError` + +Fires on internal errors (network failure, auth issue, SDK exception). + + + +```kotlin SearchActivity.kt lines +binding.cometchatSearch.onError = OnError { + Log.i(TAG, "onCreate: Error Occurred ! ${it.message}") +} +``` + + +```kotlin lines +CometChatSearch( + onError = { exception -> + Log.i(TAG, "Error: ${exception.message}") + } +) +``` + + + +#### `setOnLoadMessages` + +Fires when the message list is successfully fetched and loaded, helping track when message search results are ready. + + + +```kotlin SearchActivity.kt lines +binding.cometchatSearch.setOnLoadMessages(OnLoad { list -> + Log.i(TAG, "Messages loaded: ${list.size}") +}) +``` + + +```kotlin lines +CometChatSearch( + onLoadMessages = { messages -> + Log.i(TAG, "Messages loaded: ${messages.size}") + } +) +``` + + + +#### `setOnLoadConversations` + +Fires when the conversation list is successfully fetched and loaded, helping track when conversation search results are ready. + + + +```kotlin SearchActivity.kt lines +binding.cometchatSearch.setOnLoadConversations(OnLoad { list -> + Log.i(TAG, "Conversations loaded: ${list.size}") +}) +``` + + +```kotlin lines +CometChatSearch( + onLoadConversations = { conversations -> + Log.i(TAG, "Conversations loaded: ${conversations.size}") + } +) +``` + + + +#### `setOnEmpty` + +Fires when the search returns no results, enabling custom handling such as showing a placeholder. + + + +```kotlin SearchActivity.kt lines +binding.cometchatSearch.onEmpty = OnEmpty { + Log.i(TAG, "onCreate: No Results Found !") +} +``` + + +```kotlin lines +CometChatSearch( + onEmpty = { + Log.i(TAG, "No Results Found!") + } +) +``` + + + +- **Verify**: After setting an action callback, trigger the corresponding user interaction (tap a conversation result, tap a message result, press back, cause an error, or search for a term with no results) and confirm your custom logic executes. + +### Global UI Events + +The `CometChatSearch` component does not produce any global UI events. + +### SDK Events (Real-Time, Automatic) + +The `CometChatSearch` component does not listen to any SDK events internally. Search results are fetched on demand when the user types a query. + +## Functionality + +Small functional customizations such as toggling visibility of UI elements and configuring search scope. + +| Methods | Description | Code | +| --- | --- | --- | +| `setEmptyStateVisibility` | Hides the empty state when search returns no results | `.setEmptyStateVisibility(View.GONE);` | +| `setErrorStateVisibility` | Hides the error state on search failure | `.setErrorStateVisibility(View.GONE);` | +| `setSearchFilters` | List of filters rendered as chips in the search component | `.setSearchFilters(filters);` | +| `setInitialSearchFilter` | The filter which will be active by default on load | `.setInitialSearchFilter(UIKitConstants.SearchFilter.MESSAGES);` | +| `setSearchIn` | List of entities in which the search should be performed | `.setSearchIn(scopes);` | +| `setHideGroupType` | Hides the group type icon in conversation leading view | `.setHideGroupType(true);` | +| `setHideUserStatus` | Hides the user's online/offline status indicator | `.setHideUserStatus(true);` | + +- **Verify**: After calling a visibility or functionality method, confirm the corresponding UI element is shown or hidden, or the search scope is correctly applied. + +## Custom View Slots + +Each slot replaces a section of the default UI. Conversation item slots use the `ConversationsSearchViewHolderListener` pattern (`createView` + `bindView`). Message item slots use the `MessagesSearchViewHolderListener` pattern. + +| Slot | Method | Replaces | +| --- | --- | --- | +| Conversation item view | `setConversationItemView(ConversationsSearchViewHolderListener)` | Entire conversation item row | +| Text message item | `setTextMessageItemView(MessagesSearchViewHolderListener)` | Text message item | +| Image message item | `setImageMessageItemView(MessagesSearchViewHolderListener)` | Image message item | +| Audio message item | `setAudioMessageItemView(MessagesSearchViewHolderListener)` | Audio message item | +| Video message item | `setVideoMessageItemView(MessagesSearchViewHolderListener)` | Video message item | +| Document message item | `setDocumentMessageItemView(MessagesSearchViewHolderListener)` | Document message item | +| Link message item | `setLinkMessageItemView(MessagesSearchViewHolderListener)` | Link message item | +| Initial view | `setInitialView(@LayoutRes int)` | Initial state before search | +| Empty view | `setEmptyView(@LayoutRes int)` | Empty state | +| Loading view | `setLoadingView(View)` | Loading spinner | +| Error view | `setErrorView(View)` | Error state | + +### `setConversationItemView` + +Replace the entire conversation item row in search results. + + + +```kotlin SearchActivity.kt lines +binding.cometchatSearch.setConversationItemView(object : ConversationsSearchViewHolderListener() { + override fun createView(context: Context?, listItem: View?): View? { + return layoutInflater.inflate(R.layout.custom_conversation_search_item, null) + } + + override fun bindView( + context: Context?, + createdView: View?, + conversation: Conversation?, + holder: RecyclerView.ViewHolder?, + conversationList: List?, + position: Int + ) { + val titleTv = createdView?.findViewById(R.id.tv_title) + conversation?.let { + if (it.conversationType == CometChatConstants.CONVERSATION_TYPE_USER) { + titleTv?.text = (it.conversationWith as User).name + } else { + titleTv?.text = (it.conversationWith as Group).name + } + } + } +}) +``` + + +```kotlin lines +CometChatSearch( + conversationItemView = { conversation -> + Row(modifier = Modifier.padding(16.dp)) { + val name = when (conversation.conversationType) { + CometChatConstants.CONVERSATION_TYPE_USER -> + (conversation.conversationWith as User).name + else -> + (conversation.conversationWith as Group).name + } + Text(text = name ?: "", style = MaterialTheme.typography.bodyLarge) + } + } +) +``` + + + +> **What this does:** Registers a `ConversationsSearchViewHolderListener` for conversation items in search results. The `createView` method inflates a custom layout, and `bindView` populates it with the conversation name based on whether it's a user or group conversation. + +### `setTextMessageItemView` + +Replace the text message item view in search results. + +Define a custom layout XML: + +```xml custom_message_item_view.xml lines + + + + + + + +``` + + + +```kotlin SearchActivity.kt lines +binding.cometchatSearch.setTextMessageItemView(object : MessagesSearchViewHolderListener() { + override fun createView( + context: Context?, + listItem: View? + ): View? { + return layoutInflater.inflate(R.layout.custom_message_item_view, null) + } + + override fun bindView( + context: Context?, + createdView: View?, + message: TextMessage?, + holder: RecyclerView.ViewHolder?, + messagesList: List?, + position: Int + ) { + val titleTv = createdView?.findViewById(R.id.tv_sender_name) + val messageTv = createdView?.findViewById(R.id.tv_message) + + titleTv?.text = message?.sender?.name + messageTv?.text = message?.text + } +}) +``` + + +```kotlin lines +CometChatSearch( + textMessageItemView = { message -> + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color(0xFFE8E4F3)) + .padding(16.dp) + ) { + Text( + text = "${message.sender?.name}: ", + color = Color(0xFF6B4FBB), + fontWeight = FontWeight.Bold + ) + Text( + text = message.text ?: "", + color = Color(0xFF4A4A4A), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } +) +``` + + + +> **What this does:** Registers a `MessagesSearchViewHolderListener` for text messages. The `createView` method inflates the custom layout, and `bindView` populates the sender name and message text from the `TextMessage` object. + + + + + +### Other Message Item Views + +The following message item view methods follow the same `MessagesSearchViewHolderListener` pattern as `setTextMessageItemView`: + +| Method | Message Type | Generic Type | +| --- | --- | --- | +| `setImageMessageItemView` | Image Message | `MessagesSearchViewHolderListener` | +| `setVideoMessageItemView` | Video Message | `MessagesSearchViewHolderListener` | +| `setAudioMessageItemView` | Audio Message | `MessagesSearchViewHolderListener` | +| `setDocumentMessageItemView` | Document Message | `MessagesSearchViewHolderListener` | +| `setLinkMessageItemView` | Link Message | `MessagesSearchViewHolderListener` | + +### `setInitialView` + +Sets a custom view displayed when the search component is rendered and no search has been performed. + + + +```kotlin lines +binding.cometchatSearch.setInitialView(R.layout.your_initial_view) +``` + + +```kotlin lines +CometChatSearch( + initialView = { + // Your custom initial composable + Text("Start searching...") + } +) +``` + + + +### `setEmptyView` + +Configures a custom view displayed when the search returns no results. + + + +```kotlin lines +binding.cometchatSearch.setEmptyView(R.layout.your_empty_view) +``` + + +```kotlin lines +CometChatSearch( + emptyView = { + // Your custom empty state composable + Text("No results found") + } +) +``` + + + +### `setLoadingView` + +Sets a custom loading view displayed when search results are being fetched. + + + +```kotlin lines +binding.cometchatSearch.setLoadingView(loadingView) +``` + + +```kotlin lines +CometChatSearch( + loadingView = { + // Your custom loading composable + CircularProgressIndicator() + } +) +``` + + + +### `setErrorView` + +Defines a custom error state view that appears when an issue occurs while searching. + + + +```kotlin lines +binding.cometchatSearch.setErrorView(errorView) +``` + + +```kotlin lines +CometChatSearch( + errorView = { onRetry -> + // Your custom error composable + Column { + Text("Something went wrong") + Button(onClick = onRetry) { Text("Retry") } + } + } +) +``` + + + +- **Verify**: After setting any custom view slot, confirm the custom view renders in the correct position within the search result items, and the data binding populates correctly for each result. + +## Common Patterns + +### Messages-only search + + + +```kotlin lines +binding.cometchatSearch.setSearchFilters( + listOf(UIKitConstants.SearchFilter.MESSAGES) +) +binding.cometchatSearch.setInitialSearchFilter(UIKitConstants.SearchFilter.MESSAGES) +``` + + +```kotlin lines +import com.cometchat.uikit.core.constants.SearchFilter + +CometChatSearch( + searchFilters = listOf(SearchFilter.UNREAD), + modifier = Modifier.fillMaxSize() +) +``` + + + +### Filter by message type + + + +```kotlin lines +binding.cometchatSearch.setMessagesRequestBuilder( + MessagesRequest.MessagesRequestBuilder().setLimit(10) +) +``` + + +```kotlin lines +import com.cometchat.uikit.core.constants.SearchFilter + +CometChatSearch( + searchFilters = listOf(SearchFilter.UNREAD), + modifier = Modifier.fillMaxSize() +) +``` + + + +### Scope search to a specific user + + + +```kotlin lines +binding.cometchatSearch.setUid("user123") +``` + + +```kotlin lines +CometChatSearch( + uid = "user123", + modifier = Modifier.fillMaxSize() +) +``` + + + +### Scope search to a specific group + + + +```kotlin lines +binding.cometchatSearch.setGuid("group123") +``` + + +```kotlin lines +CometChatSearch( + guid = "group123", + modifier = Modifier.fillMaxSize() +) +``` + + + +## Advanced Methods + +### Search Scope + +#### `setUid` + +Scopes the search to a specific user's conversation. + + + +```kotlin lines +binding.cometchatSearch.setUid("user123") +``` + + +```kotlin lines +CometChatSearch( + uid = "user123", + modifier = Modifier.fillMaxSize() +) +``` + + + +#### `setGuid` + +Scopes the search to a specific group's conversation. + + + +```kotlin lines +binding.cometchatSearch.setGuid("group123") +``` + + +```kotlin lines +CometChatSearch( + guid = "group123", + modifier = Modifier.fillMaxSize() +) +``` + + + +### Request Builders + +#### `setConversationsRequestBuilder` + +Sets a `ConversationsRequest.ConversationsRequestBuilder` to filter conversation search results. Pass the builder instance — not the result of `.build()`. For all available builder options, refer to the [ConversationRequestBuilder](/sdk/android/retrieve-conversations) documentation. + + + +```kotlin lines +binding.cometchatSearch.setConversationsRequestBuilder( + ConversationsRequest.ConversationsRequestBuilder().setLimit(10) +) +``` + + +```kotlin lines +CometChatSearch( + guid = "group123", + modifier = Modifier.fillMaxSize() +) +``` + + + +#### `setMessagesRequestBuilder` + +Sets a `MessagesRequest.MessagesRequestBuilder` to filter message search results. For all available builder options, refer to the [MessagesRequestBuilder](/sdk/android/additional-message-filtering) documentation. + + + +```kotlin lines +binding.cometchatSearch.setMessagesRequestBuilder( + MessagesRequest.MessagesRequestBuilder().setLimit(5) +) +``` + + +```kotlin lines +CometChatSearch( + guid = "group123", + modifier = Modifier.fillMaxSize() +) +``` + + + +### Text Formatters + +#### `setTextFormatters` + +Enables developers to define and apply text formatters that dynamically modify or transform message content before rendering it in the UI. Text formatters can be used for: + +* Automatically converting URLs into clickable links +* Applying Markdown or rich text styling +* Replacing certain words or patterns with emojis or predefined text +* Censoring specific words for moderation + +For implementation details, refer to the [MentionsFormatter Guide](/ui-kit/android/v6/mentions-formatter-guide). + +### Date/Time Formatting + +#### `setDateTimeFormatter` + +Provides a custom implementation of `DateTimeFormatterCallback` to configure how time and date values are displayed in search results. + + + +```kotlin lines +import java.text.SimpleDateFormat +import java.util.* + +binding.cometchatSearch.setDateTimeFormatter(object : DateTimeFormatterCallback { + + private val fullTimeFormatter = SimpleDateFormat("hh:mm a", Locale.getDefault()) + private val dateFormatter = SimpleDateFormat("dd MMM yyyy", Locale.getDefault()) + + override fun time(timestamp: Long): String { + return fullTimeFormatter.format(Date(timestamp)) + } + + override fun today(timestamp: Long): String { + return "Today" + } + + override fun yesterday(timestamp: Long): String { + return "Yesterday" + } + + override fun lastWeek(timestamp: Long): String { + return "Last Week" + } + + override fun otherDays(timestamp: Long): String { + return dateFormatter.format(Date(timestamp)) + } + + override fun minutes(diffInMinutesFromNow: Long, timestamp: Long): String { + return "$diffInMinutesFromNow mins ago" + } + + override fun hours(diffInHourFromNow: Long, timestamp: Long): String { + return "$diffInHourFromNow hrs ago" + } +}) +``` + + +```kotlin lines +CometChatSearch( + guid = "group123", + modifier = Modifier.fillMaxSize() +) +``` + + + +> **What this does:** Provides a custom date/time formatter that displays "Today", "Yesterday", "Last Week" for recent messages, a "dd MMM yyyy" format for older messages, and relative time strings like "5 mins ago" or "2 hrs ago" for recent activity. + +### Other Advanced Methods + +| Method | Type | Description | +| --- | --- | --- | +| `setMentionAllLabelId` | `String id, String mentionAllLabel` | Sets a custom label for the "mention all" feature for a specific ID | +| `setHintText` | `String` | Sets the hint text displayed in the search bar | + +## Style + +The component uses XML theme styles. Define a custom style with parent `CometChatSearchStyle` in `themes.xml`, then apply with `setStyle()`. + + + + + +```xml themes.xml lines + + + +``` + +> **What this does:** Defines a custom search style that sets a light purple background color (`#EDEAFA`) for the search component, conversation items, and message items, and applies a Times New Roman font to all text elements including filter chips, section headers, conversation titles/subtitles, message titles/subtitles, timestamps, "see more" text, and the search bar. + + + +```kotlin lines +binding.cometchatSearch.setStyle(R.style.CustomSearchStyle) +``` + + +```kotlin lines +import com.cometchat.uikit.compose.presentation.search.style.CometChatSearchStyle + +CometChatSearch( + style = CometChatSearchStyle.default().copy( + backgroundColor = Color(0xFFEDEAFA), + searchBarBackgroundColor = Color(0xFFEDEAFA), + filterChipBackgroundColor = Color(0xFFEDEAFA) + ), + modifier = Modifier.fillMaxSize() +) +``` + + + +To view all available style attributes, visit the [attributes file](https://github.com/cometchat/cometchat-uikit-android/blob/v6/chatuikit/src/main/res/values/attr_cometchat_search.xml). + +### Programmatic Style Properties + +In addition to XML theme styles, the component exposes programmatic setters for fine-grained control: + +**Search Bar** + +| Method | Type | Description | +| --- | --- | --- | +| `setBackgroundColor` | `@ColorInt int` | Background color of the component | +| `setSearchBarBackgroundColor` | `@ColorInt int` | Background color of the search bar | +| `setSearchBarStrokeWidth` | `@Dimension int` | Stroke width of the search bar border | +| `setSearchBarStrokeColor` | `@ColorInt int` | Stroke color of the search bar border | +| `setSearchBarCornerRadius` | `@Dimension int` | Corner radius of the search bar | +| `setSearchBarTextColor` | `@ColorInt int` | Text color of the search bar input | +| `setSearchBarTextAppearance` | `@StyleRes int` | Text appearance of the search bar input | +| `setSearchBarHintTextColor` | `@ColorInt int` | Hint text color of the search bar | +| `setBackIcon` | `Drawable` | Custom back icon drawable | +| `setBackIconTint` | `@ColorInt int` | Tint color for the back icon | +| `setClearIcon` | `Drawable` | Custom clear icon drawable | +| `setClearIconTint` | `@ColorInt int` | Tint color for the clear icon | +| `setSearchIcon` | `Drawable` | Custom search icon drawable | +| `setSearchIconTint` | `@ColorInt int` | Tint color for the search icon | + +**Filter Chips** + +| Method | Type | Description | +| --- | --- | --- | +| `setFilterChipBackgroundColor` | `@ColorInt int` | Background color of filter chips | +| `setFilterChipSelectedBackgroundColor` | `@ColorInt int` | Background color of selected filter chips | +| `setFilterChipTextColor` | `@ColorInt int` | Text color of filter chips | +| `setFilterChipSelectedTextColor` | `@ColorInt int` | Text color of selected filter chips | +| `setFilterChipTextAppearance` | `@StyleRes int` | Text appearance of filter chips | +| `setFilterChipStrokeColor` | `@ColorInt int` | Stroke color of filter chips | +| `setFilterChipSelectedStrokeColor` | `@ColorInt int` | Stroke color of selected filter chips | +| `setFilterChipStrokeWidth` | `@Dimension int` | Stroke width of filter chips | +| `setFilterChipCornerRadius` | `@Dimension int` | Corner radius of filter chips | + +**Section Headers** + +| Method | Type | Description | +| --- | --- | --- | +| `setSectionHeaderTextColor` | `@ColorInt int` | Text color for section headers | +| `setSectionHeaderTextAppearance` | `@StyleRes int` | Text appearance for section headers | +| `setSectionHeaderBackgroundColor` | `@ColorInt int` | Background color for section headers | + +**Conversation Items** + +| Method | Type | Description | +| --- | --- | --- | +| `setConversationItemBackgroundColor` | `@ColorInt int` | Background color for conversation items | +| `setConversationTitleTextColor` | `@ColorInt int` | Text color for conversation titles | +| `setConversationTitleTextAppearance` | `@StyleRes int` | Text appearance for conversation titles | +| `setConversationSubtitleTextColor` | `@ColorInt int` | Text color for conversation subtitles | +| `setConversationSubtitleTextAppearance` | `@StyleRes int` | Text appearance for conversation subtitles | +| `setConversationTimestampTextColor` | `@ColorInt int` | Text color for conversation timestamps | +| `setConversationTimestampTextAppearance` | `@StyleRes int` | Text appearance for conversation timestamps | + +**Message Items** + +| Method | Type | Description | +| --- | --- | --- | +| `setMessageItemBackgroundColor` | `@ColorInt int` | Background color for message items | +| `setMessageTitleTextColor` | `@ColorInt int` | Text color for message titles | +| `setMessageTitleTextAppearance` | `@StyleRes int` | Text appearance for message titles | +| `setMessageSubtitleTextColor` | `@ColorInt int` | Text color for message subtitles | +| `setMessageSubtitleTextAppearance` | `@StyleRes int` | Text appearance for message subtitles | +| `setMessageTimestampTextColor` | `@ColorInt int` | Text color for message timestamps | +| `setMessageTimestampTextAppearance` | `@StyleRes int` | Text appearance for message timestamps | +| `setMessageLinkTextColor` | `@ColorInt int` | Text color for links in messages | +| `setMessageLinkTextAppearance` | `@StyleRes int` | Text appearance for links in messages | + +**Other Style Properties** + +| Method | Type | Description | +| --- | --- | --- | +| `setAvatarStyle` | `@StyleRes int` | Style for avatars in search results | +| `setBadgeStyle` | `@StyleRes int` | Style for badges in search results | +| `setSeeMoreTextColor` | `@ColorInt int` | Text color for "See more" links | +| `setSeeMoreTextAppearance` | `@StyleRes int` | Text appearance for "See more" links | +| `setDateSeparatorTextColor` | `@ColorInt int` | Text color for date separators | +| `setDateSeparatorBackgroundColor` | `@ColorInt int` | Background color for date separators | +| `setDateSeparatorTextAppearance` | `@StyleRes int` | Text appearance for date separators | +| `setEmptyStateTextColor` | `@ColorInt int` | Title text color for the empty state | +| `setEmptyStateTextAppearance` | `@StyleRes int` | Title text appearance for the empty state | +| `setEmptyStateSubtitleTextColor` | `@ColorInt int` | Subtitle text color for the empty state | +| `setEmptyStateSubtitleTextAppearance` | `@StyleRes int` | Subtitle text appearance for the empty state | +| `setEmptyStateIcon` | `Drawable` | Icon for the empty state | +| `setEmptyStateIconTint` | `@ColorInt int` | Tint for the empty state icon | +| `setErrorStateTextColor` | `@ColorInt int` | Title text color for the error state | +| `setErrorStateTextAppearance` | `@StyleRes int` | Title text appearance for the error state | +| `setErrorStateSubtitleTextColor` | `@ColorInt int` | Subtitle text color for the error state | +| `setErrorStateSubtitleTextAppearance` | `@StyleRes int` | Subtitle text appearance for the error state | +| `setErrorStateIcon` | `Drawable` | Icon for the error state | +| `setErrorStateIconTint` | `@ColorInt int` | Tint for the error state icon | + +## Customization Matrix + +| What to change | Where | Property/API | Example | +| --- | --- | --- | --- | +| Override behavior on conversation tap | Activity/Fragment | `setOnConversationClicked` | `setOnConversationClicked { v, pos, conv -> ... }` | +| Override behavior on message tap | Activity/Fragment | `setOnMessageClicked` | `setOnMessageClicked { v, pos, msg -> ... }` | +| Override back press behavior | Activity/Fragment | `setOnBackPressListener` | `setOnBackPressListener { ... }` | +| Scope search to a user | Activity/Fragment | `setUid(String)` | `.setUid("user123")` | +| Scope search to a group | Activity/Fragment | `setGuid(String)` | `.setGuid("group123")` | +| Hide user online status | Activity/Fragment | `setHideUserStatus(boolean)` | `.setHideUserStatus(true)` | +| Hide group type icon | Activity/Fragment | `setHideGroupType(boolean)` | `.setHideGroupType(true)` | +| Set search filters | Activity/Fragment | `setSearchFilters(List)` | `.setSearchFilters(filters)` | +| Set initial active filter | Activity/Fragment | `setInitialSearchFilter(SearchFilter)` | `.setInitialSearchFilter(UIKitConstants.SearchFilter.MESSAGES)` | +| Set search scope | Activity/Fragment | `setSearchIn(List)` | `.setSearchIn(scopes)` | +| Filter conversation results | Activity/Fragment | `setConversationsRequestBuilder` | See Request Builders code above | +| Filter message results | Activity/Fragment | `setMessagesRequestBuilder` | See Request Builders code above | +| Custom conversation item view | Activity/Fragment | `setConversationItemView(ConversationsSearchViewHolderListener)` | See `setConversationItemView` code above | +| Custom text message item view | Activity/Fragment | `setTextMessageItemView(MessagesSearchViewHolderListener)` | See `setTextMessageItemView` code above | +| Custom image message item view | Activity/Fragment | `setImageMessageItemView(MessagesSearchViewHolderListener)` | See `setTextMessageItemView` pattern | +| Custom video message item view | Activity/Fragment | `setVideoMessageItemView(MessagesSearchViewHolderListener)` | See `setTextMessageItemView` pattern | +| Custom audio message item view | Activity/Fragment | `setAudioMessageItemView(MessagesSearchViewHolderListener)` | See `setTextMessageItemView` pattern | +| Custom document message item view | Activity/Fragment | `setDocumentMessageItemView(MessagesSearchViewHolderListener)` | See `setTextMessageItemView` pattern | +| Custom link message item view | Activity/Fragment | `setLinkMessageItemView(MessagesSearchViewHolderListener)` | See `setTextMessageItemView` pattern | +| Custom initial view | Activity/Fragment | `setInitialView(@LayoutRes int)` | `.setInitialView(R.layout.your_initial_view)` | +| Custom loading view | Activity/Fragment | `setLoadingView(View)` | `.setLoadingView(loadingView)` | +| Custom empty view | Activity/Fragment | `setEmptyView(@LayoutRes int)` | `.setEmptyView(R.layout.your_empty_view)` | +| Custom error view | Activity/Fragment | `setErrorView(View)` | `.setErrorView(errorView)` | +| Empty state visibility | Activity/Fragment | `setEmptyStateVisibility(int)` | `.setEmptyStateVisibility(View.GONE)` | +| Error state visibility | Activity/Fragment | `setErrorStateVisibility(int)` | `.setErrorStateVisibility(View.GONE)` | +| Change colors, fonts, spacing | `themes.xml` | `CometChatSearchStyle` | `#EDEAFA` | +| Apply a custom style | Activity/Fragment | `setStyle(int styleRes)` | `binding.cometchatSearch.setStyle(R.style.CustomSearchStyle)` | +| Search bar background | Activity/Fragment | `setSearchBarBackgroundColor` | `.setSearchBarBackgroundColor(color)` | +| Filter chip colors | Activity/Fragment | `setFilterChip*` methods | `.setFilterChipBackgroundColor(color)` | +| Section header style | Activity/Fragment | `setSectionHeader*` methods | `.setSectionHeaderTextColor(color)` | +| Conversation item style | Activity/Fragment | `setConversation*` methods | `.setConversationTitleTextColor(color)` | +| Message item style | Activity/Fragment | `setMessage*` methods | `.setMessageTitleTextColor(color)` | +| Date/time formatting | Activity/Fragment | `setDateTimeFormatter(DateTimeFormatterCallback)` | See `setDateTimeFormatter` code above | +| Text formatters | Activity/Fragment | `setTextFormatters(List)` | See Text Formatters section | +| Search bar hint text | Activity/Fragment | `setHintText(String)` | `.setHintText("Search...")` | +| Mention-all label | Activity/Fragment | `setMentionAllLabelId(String, String)` | `.setMentionAllLabelId(id, label)` | +| Callback on messages loaded | Activity/Fragment | `setOnLoadMessages(OnLoad)` | `.setOnLoadMessages { list -> ... }` | +| Callback on conversations loaded | Activity/Fragment | `setOnLoadConversations(OnLoad)` | `.setOnLoadConversations { list -> ... }` | + +## Next Steps + + + + Browse recent conversations + + + Browse and search available users + + + Browse and search available groups + + + Display messages in a conversation + + \ No newline at end of file From 4261a37d58fe503a1a7f1600aa29bb63df5f0d03 Mon Sep 17 00:00:00 2001 From: Anshuman Pathania Date: Thu, 7 May 2026 19:18:04 +0530 Subject: [PATCH 02/61] release: make Flutter SDK v5 the default, move v4 to /v4/ path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move sdk/flutter/*.mdx → sdk/flutter/v4/ (53 files) - Move sdk/flutter/v5/*.mdx → sdk/flutter/ (51 files) - Update docs.json: v5 first in dropdown (default), v4 second - Update all navigation paths accordingly - Remove all beta references from v5 docs - Version 5.0.0 stable Committed-by: kiro-bot --- docs.json | 250 +-- sdk/flutter/additional-message-filtering.mdx | 284 ++-- sdk/flutter/{v5 => }/advanced-overview.mdx | 0 sdk/flutter/ai-agents.mdx | 172 +- sdk/flutter/ai-chatbots-overview.mdx | 61 - sdk/flutter/ai-moderation.mdx | 144 +- sdk/flutter/ai-user-copilot-overview.mdx | 4 +- sdk/flutter/authentication-overview.mdx | 309 +--- sdk/flutter/block-users.mdx | 173 +- sdk/flutter/changelog.mdx | 2 - sdk/flutter/connection-behaviour.mdx | 125 -- sdk/flutter/connection-status.mdx | 127 +- sdk/flutter/create-group.mdx | 201 +-- sdk/flutter/delete-conversation.mdx | 90 +- sdk/flutter/delete-group.mdx | 85 +- sdk/flutter/delete-message.mdx | 201 +-- sdk/flutter/delivery-read-receipts.mdx | 346 ++-- sdk/flutter/edit-message.mdx | 230 +-- sdk/flutter/extensions-overview.mdx | 142 +- sdk/flutter/flag-message.mdx | 123 +- sdk/flutter/{v5 => }/flutter-overview.mdx | 0 sdk/flutter/group-add-members.mdx | 133 +- sdk/flutter/group-change-member-scope.mdx | 115 +- sdk/flutter/{v5 => }/group-kick-member.mdx | 0 sdk/flutter/groups-overview.mdx | 114 +- sdk/flutter/interactive-messages.mdx | 473 +----- sdk/flutter/join-group.mdx | 120 +- sdk/flutter/leave-group.mdx | 92 +- sdk/flutter/{v5 => }/login-listeners.mdx | 0 sdk/flutter/mentions.mdx | 373 +---- sdk/flutter/{v5 => }/messaging-overview.mdx | 0 sdk/flutter/overview.mdx | 192 +-- sdk/flutter/reactions.mdx | 521 +----- sdk/flutter/real-time-listeners.mdx | 508 +----- sdk/flutter/receive-messages.mdx | 771 +-------- sdk/flutter/{v5 => }/resources-overview.mdx | 0 sdk/flutter/retrieve-conversations.mdx | 688 ++------ sdk/flutter/retrieve-group-members.mdx | 128 +- sdk/flutter/retrieve-groups.mdx | 186 +-- sdk/flutter/retrieve-users.mdx | 312 ++-- sdk/flutter/send-message.mdx | 864 ++-------- sdk/flutter/setup.mdx | 220 +-- sdk/flutter/threaded-messages.mdx | 432 +---- sdk/flutter/transfer-group-ownership.mdx | 72 +- sdk/flutter/transient-messages.mdx | 138 +- sdk/flutter/typing-indicators.mdx | 128 +- sdk/flutter/update-group.mdx | 97 +- .../{v5 => }/upgrading-from-v4-guide.mdx | 6 +- sdk/flutter/user-management.mdx | 260 +-- sdk/flutter/user-presence.mdx | 99 +- sdk/flutter/users-overview.mdx | 50 +- .../v4/additional-message-filtering.mdx | 660 ++++++++ sdk/flutter/v4/ai-agents.mdx | 216 +++ sdk/flutter/v4/ai-chatbots-overview.mdx | 65 + sdk/flutter/v4/ai-moderation.mdx | 317 ++++ sdk/flutter/v4/ai-user-copilot-overview.mdx | 6 + sdk/flutter/v4/authentication-overview.mdx | 348 ++++ sdk/flutter/{ => v4}/best-practices.mdx | 0 sdk/flutter/v4/block-users.mdx | 307 ++++ sdk/flutter/{ => v4}/calling-overview.mdx | 0 sdk/flutter/{v5 => v4}/changelog.mdx | 2 + .../{v5 => v4}/connection-behaviour.mdx | 125 ++ sdk/flutter/v4/connection-status.mdx | 161 ++ sdk/flutter/v4/create-group.mdx | 249 +++ sdk/flutter/v4/delete-conversation.mdx | 125 ++ sdk/flutter/v4/delete-group.mdx | 104 ++ sdk/flutter/v4/delete-message.mdx | 272 ++++ sdk/flutter/v4/delivery-read-receipts.mdx | 418 +++++ sdk/flutter/v4/edit-message.mdx | 296 ++++ sdk/flutter/{ => v4}/error-codes.mdx | 0 sdk/flutter/v4/extensions-overview.mdx | 144 ++ sdk/flutter/{v5 => v4}/flag-message.mdx | 123 +- sdk/flutter/v4/group-add-members.mdx | 178 +++ sdk/flutter/v4/group-change-member-scope.mdx | 151 ++ .../{ => v4}/group-kick-ban-members.mdx | 0 sdk/flutter/v4/groups-overview.mdx | 75 + sdk/flutter/v4/interactive-messages.mdx | 579 +++++++ sdk/flutter/v4/join-group.mdx | 166 ++ sdk/flutter/{ => v4}/key-concepts.mdx | 0 sdk/flutter/v4/leave-group.mdx | 149 ++ sdk/flutter/v4/mentions.mdx | 596 +++++++ .../message-structure-and-hierarchy.mdx | 0 sdk/flutter/v4/overview.mdx | 149 ++ sdk/flutter/{ => v4}/rate-limits.mdx | 0 sdk/flutter/v4/reactions.mdx | 576 +++++++ sdk/flutter/v4/real-time-listeners.mdx | 639 ++++++++ sdk/flutter/v4/receive-messages.mdx | 1410 +++++++++++++++++ sdk/flutter/v4/retrieve-conversations.mdx | 864 ++++++++++ sdk/flutter/v4/retrieve-group-members.mdx | 222 +++ sdk/flutter/v4/retrieve-groups.mdx | 346 ++++ sdk/flutter/v4/retrieve-users.mdx | 453 ++++++ sdk/flutter/v4/send-message.mdx | 1264 +++++++++++++++ sdk/flutter/v4/setup.mdx | 246 +++ sdk/flutter/v4/threaded-messages.mdx | 536 +++++++ sdk/flutter/v4/transfer-group-ownership.mdx | 95 ++ sdk/flutter/v4/transient-messages.mdx | 167 ++ sdk/flutter/{ => v4}/troubleshooting.mdx | 0 sdk/flutter/v4/typing-indicators.mdx | 181 +++ sdk/flutter/v4/update-group.mdx | 128 ++ .../{ => v4}/upgrading-from-v3-guide.mdx | 0 sdk/flutter/v4/user-management.mdx | 282 ++++ sdk/flutter/v4/user-presence.mdx | 152 ++ sdk/flutter/v4/users-overview.mdx | 37 + sdk/flutter/v4/webhooks-overview.mdx | 115 ++ .../v5/additional-message-filtering.mdx | 598 ------- sdk/flutter/v5/ai-agents.mdx | 114 -- sdk/flutter/v5/ai-chatbots-overview.mdx | 4 - sdk/flutter/v5/ai-moderation.mdx | 175 -- sdk/flutter/v5/ai-user-copilot-overview.mdx | 4 - sdk/flutter/v5/authentication-overview.mdx | 151 -- sdk/flutter/v5/block-users.mdx | 140 -- sdk/flutter/v5/connection-status.mdx | 86 - sdk/flutter/v5/create-group.mdx | 134 -- sdk/flutter/v5/delete-conversation.mdx | 53 - sdk/flutter/v5/delete-group.mdx | 31 - sdk/flutter/v5/delete-message.mdx | 89 -- sdk/flutter/v5/delivery-read-receipts.mdx | 280 ---- sdk/flutter/v5/edit-message.mdx | 112 -- sdk/flutter/v5/extensions-overview.mdx | 4 - sdk/flutter/v5/group-add-members.mdx | 85 - sdk/flutter/v5/group-change-member-scope.mdx | 78 - sdk/flutter/v5/groups-overview.mdx | 61 - sdk/flutter/v5/interactive-messages.mdx | 248 --- sdk/flutter/v5/join-group.mdx | 76 - sdk/flutter/v5/leave-group.mdx | 67 - sdk/flutter/v5/mentions.mdx | 227 --- sdk/flutter/v5/overview.mdx | 98 -- sdk/flutter/v5/reactions.mdx | 213 --- sdk/flutter/v5/real-time-listeners.mdx | 249 --- sdk/flutter/v5/receive-messages.mdx | 641 -------- sdk/flutter/v5/retrieve-conversations.mdx | 384 ----- sdk/flutter/v5/retrieve-group-members.mdx | 116 -- sdk/flutter/v5/retrieve-groups.mdx | 174 -- sdk/flutter/v5/retrieve-users.mdx | 337 ---- sdk/flutter/v5/send-message.mdx | 646 -------- sdk/flutter/v5/setup.mdx | 173 -- sdk/flutter/v5/threaded-messages.mdx | 150 -- sdk/flutter/v5/transfer-group-ownership.mdx | 29 - sdk/flutter/v5/transient-messages.mdx | 65 - sdk/flutter/v5/typing-indicators.mdx | 107 -- sdk/flutter/v5/update-group.mdx | 43 - sdk/flutter/v5/user-management.mdx | 102 -- sdk/flutter/v5/user-presence.mdx | 83 - sdk/flutter/v5/users-overview.mdx | 35 - sdk/flutter/v5/webhooks-overview.mdx | 4 - sdk/flutter/webhooks-overview.mdx | 113 +- 146 files changed, 15216 insertions(+), 15218 deletions(-) rename sdk/flutter/{v5 => }/advanced-overview.mdx (100%) rename sdk/flutter/{v5 => }/flutter-overview.mdx (100%) rename sdk/flutter/{v5 => }/group-kick-member.mdx (100%) rename sdk/flutter/{v5 => }/login-listeners.mdx (100%) rename sdk/flutter/{v5 => }/messaging-overview.mdx (100%) rename sdk/flutter/{v5 => }/resources-overview.mdx (100%) rename sdk/flutter/{v5 => }/upgrading-from-v4-guide.mdx (94%) create mode 100644 sdk/flutter/v4/additional-message-filtering.mdx create mode 100644 sdk/flutter/v4/ai-agents.mdx create mode 100644 sdk/flutter/v4/ai-chatbots-overview.mdx create mode 100644 sdk/flutter/v4/ai-moderation.mdx create mode 100644 sdk/flutter/v4/ai-user-copilot-overview.mdx create mode 100644 sdk/flutter/v4/authentication-overview.mdx rename sdk/flutter/{ => v4}/best-practices.mdx (100%) create mode 100644 sdk/flutter/v4/block-users.mdx rename sdk/flutter/{ => v4}/calling-overview.mdx (100%) rename sdk/flutter/{v5 => v4}/changelog.mdx (53%) rename sdk/flutter/{v5 => v4}/connection-behaviour.mdx (60%) create mode 100644 sdk/flutter/v4/connection-status.mdx create mode 100644 sdk/flutter/v4/create-group.mdx create mode 100644 sdk/flutter/v4/delete-conversation.mdx create mode 100644 sdk/flutter/v4/delete-group.mdx create mode 100644 sdk/flutter/v4/delete-message.mdx create mode 100644 sdk/flutter/v4/delivery-read-receipts.mdx create mode 100644 sdk/flutter/v4/edit-message.mdx rename sdk/flutter/{ => v4}/error-codes.mdx (100%) create mode 100644 sdk/flutter/v4/extensions-overview.mdx rename sdk/flutter/{v5 => v4}/flag-message.mdx (54%) create mode 100644 sdk/flutter/v4/group-add-members.mdx create mode 100644 sdk/flutter/v4/group-change-member-scope.mdx rename sdk/flutter/{ => v4}/group-kick-ban-members.mdx (100%) create mode 100644 sdk/flutter/v4/groups-overview.mdx create mode 100644 sdk/flutter/v4/interactive-messages.mdx create mode 100644 sdk/flutter/v4/join-group.mdx rename sdk/flutter/{ => v4}/key-concepts.mdx (100%) create mode 100644 sdk/flutter/v4/leave-group.mdx create mode 100644 sdk/flutter/v4/mentions.mdx rename sdk/flutter/{ => v4}/message-structure-and-hierarchy.mdx (100%) create mode 100644 sdk/flutter/v4/overview.mdx rename sdk/flutter/{ => v4}/rate-limits.mdx (100%) create mode 100644 sdk/flutter/v4/reactions.mdx create mode 100644 sdk/flutter/v4/real-time-listeners.mdx create mode 100644 sdk/flutter/v4/receive-messages.mdx create mode 100644 sdk/flutter/v4/retrieve-conversations.mdx create mode 100644 sdk/flutter/v4/retrieve-group-members.mdx create mode 100644 sdk/flutter/v4/retrieve-groups.mdx create mode 100644 sdk/flutter/v4/retrieve-users.mdx create mode 100644 sdk/flutter/v4/send-message.mdx create mode 100644 sdk/flutter/v4/setup.mdx create mode 100644 sdk/flutter/v4/threaded-messages.mdx create mode 100644 sdk/flutter/v4/transfer-group-ownership.mdx create mode 100644 sdk/flutter/v4/transient-messages.mdx rename sdk/flutter/{ => v4}/troubleshooting.mdx (100%) create mode 100644 sdk/flutter/v4/typing-indicators.mdx create mode 100644 sdk/flutter/v4/update-group.mdx rename sdk/flutter/{ => v4}/upgrading-from-v3-guide.mdx (100%) create mode 100644 sdk/flutter/v4/user-management.mdx create mode 100644 sdk/flutter/v4/user-presence.mdx create mode 100644 sdk/flutter/v4/users-overview.mdx create mode 100644 sdk/flutter/v4/webhooks-overview.mdx delete mode 100644 sdk/flutter/v5/additional-message-filtering.mdx delete mode 100644 sdk/flutter/v5/ai-agents.mdx delete mode 100644 sdk/flutter/v5/ai-chatbots-overview.mdx delete mode 100644 sdk/flutter/v5/ai-moderation.mdx delete mode 100644 sdk/flutter/v5/ai-user-copilot-overview.mdx delete mode 100644 sdk/flutter/v5/authentication-overview.mdx delete mode 100644 sdk/flutter/v5/block-users.mdx delete mode 100644 sdk/flutter/v5/connection-status.mdx delete mode 100644 sdk/flutter/v5/create-group.mdx delete mode 100644 sdk/flutter/v5/delete-conversation.mdx delete mode 100644 sdk/flutter/v5/delete-group.mdx delete mode 100644 sdk/flutter/v5/delete-message.mdx delete mode 100644 sdk/flutter/v5/delivery-read-receipts.mdx delete mode 100644 sdk/flutter/v5/edit-message.mdx delete mode 100644 sdk/flutter/v5/extensions-overview.mdx delete mode 100644 sdk/flutter/v5/group-add-members.mdx delete mode 100644 sdk/flutter/v5/group-change-member-scope.mdx delete mode 100644 sdk/flutter/v5/groups-overview.mdx delete mode 100644 sdk/flutter/v5/interactive-messages.mdx delete mode 100644 sdk/flutter/v5/join-group.mdx delete mode 100644 sdk/flutter/v5/leave-group.mdx delete mode 100644 sdk/flutter/v5/mentions.mdx delete mode 100644 sdk/flutter/v5/overview.mdx delete mode 100644 sdk/flutter/v5/reactions.mdx delete mode 100644 sdk/flutter/v5/real-time-listeners.mdx delete mode 100644 sdk/flutter/v5/receive-messages.mdx delete mode 100644 sdk/flutter/v5/retrieve-conversations.mdx delete mode 100644 sdk/flutter/v5/retrieve-group-members.mdx delete mode 100644 sdk/flutter/v5/retrieve-groups.mdx delete mode 100644 sdk/flutter/v5/retrieve-users.mdx delete mode 100644 sdk/flutter/v5/send-message.mdx delete mode 100644 sdk/flutter/v5/setup.mdx delete mode 100644 sdk/flutter/v5/threaded-messages.mdx delete mode 100644 sdk/flutter/v5/transfer-group-ownership.mdx delete mode 100644 sdk/flutter/v5/transient-messages.mdx delete mode 100644 sdk/flutter/v5/typing-indicators.mdx delete mode 100644 sdk/flutter/v5/update-group.mdx delete mode 100644 sdk/flutter/v5/user-management.mdx delete mode 100644 sdk/flutter/v5/user-presence.mdx delete mode 100644 sdk/flutter/v5/users-overview.mdx delete mode 100644 sdk/flutter/v5/webhooks-overview.mdx diff --git a/docs.json b/docs.json index a014a3746..86121f5c4 100644 --- a/docs.json +++ b/docs.json @@ -916,7 +916,7 @@ "icon": "/images/icons/react.svg", "versions": [ { - "version": "v5\u200e", + "version": "v5‎", "groups": [ { "group": " ", @@ -1027,7 +1027,7 @@ ] }, { - "version": "v4\u200e", + "version": "v4‎", "groups": [ { "group": " ", @@ -1190,7 +1190,7 @@ ] }, { - "version": "v3\u200e", + "version": "v3‎", "groups": [ { "group": " ", @@ -1212,7 +1212,7 @@ ] }, { - "version": "v2\u200e", + "version": "v2‎", "groups": [ { "group": " ", @@ -1240,7 +1240,7 @@ "icon": "/images/icons/swift.svg", "versions": [ { - "version": "v5\u200e\u200e", + "version": "v5‎‎", "groups": [ { "group": " ", @@ -1347,7 +1347,7 @@ ] }, { - "version": "v4\u200e\u200e", + "version": "v4‎‎", "groups": [ { "group": " ", @@ -1511,7 +1511,7 @@ ] }, { - "version": "v3\u200e\u200e", + "version": "v3‎‎", "groups": [ { "group": " ", @@ -1533,7 +1533,7 @@ ] }, { - "version": "v2\u200e\u200e", + "version": "v2‎‎", "groups": [ { "group": " ", @@ -1561,7 +1561,7 @@ "icon": "/images/icons/android.svg", "versions": [ { - "version": "v5\u200e\u200e\u200e", + "version": "v5‎‎‎", "groups": [ { "group": " ", @@ -1678,7 +1678,7 @@ ] }, { - "version": "v6\u200e\u200e\u200e", + "version": "v6‎‎‎", "groups": [ { "group": " ", @@ -1791,7 +1791,7 @@ ] }, { - "version": "v4\u200e\u200e\u200e", + "version": "v4‎‎‎", "groups": [ { "group": " ", @@ -1948,7 +1948,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e", + "version": "v3‎‎‎", "groups": [ { "group": " ", @@ -1973,7 +1973,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e", + "version": "v2‎‎‎", "groups": [ { "group": " ", @@ -2002,7 +2002,7 @@ "icon": "/images/icons/flutter.svg", "versions": [ { - "version": "v5\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎", "groups": [ { "group": " ", @@ -2106,7 +2106,7 @@ ] }, { - "version": "v6\u200e\u200e\u200e\u200e", + "version": "v6‎‎‎‎", "groups": [ { "group": " ", @@ -2220,7 +2220,7 @@ ] }, { - "version": "v4\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎", "groups": [ { "group": " ", @@ -2781,7 +2781,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎", "groups": [ { "group": " ", @@ -2802,7 +2802,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎", "groups": [ { "group": " ", @@ -2829,7 +2829,7 @@ "icon": "/images/icons/vuejs.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -2922,7 +2922,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -2944,7 +2944,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -2976,7 +2976,7 @@ "icon": "/images/icons/js.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3068,7 +3068,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3191,7 +3191,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3294,7 +3294,7 @@ "icon": "/images/icons/react.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3382,7 +3382,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3496,7 +3496,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3598,7 +3598,7 @@ "icon": "/images/icons/swift.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3687,7 +3687,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3805,7 +3805,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3912,7 +3912,7 @@ "icon": "/images/icons/android.svg", "versions": [ { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4013,7 +4013,7 @@ ] }, { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4107,7 +4107,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4227,7 +4227,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4330,17 +4330,24 @@ "icon": "/images/icons/flutter.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", "pages": [ "sdk/flutter/overview", "sdk/flutter/setup", - "sdk/flutter/authentication-overview", + { + "group": "Authentication", + "pages": [ + "sdk/flutter/authentication-overview", + "sdk/flutter/login-listeners" + ] + }, { "group": "Messaging", "pages": [ + "sdk/flutter/messaging-overview", "sdk/flutter/send-message", "sdk/flutter/receive-messages", "sdk/flutter/additional-message-filtering", @@ -4358,7 +4365,6 @@ "sdk/flutter/reactions" ] }, - "sdk/flutter/calling-overview", { "group": "Users", "pages": [ @@ -4381,7 +4387,7 @@ "sdk/flutter/delete-group", "sdk/flutter/retrieve-group-members", "sdk/flutter/group-add-members", - "sdk/flutter/group-kick-ban-members", + "sdk/flutter/group-kick-member", "sdk/flutter/group-change-member-scope", "sdk/flutter/transfer-group-ownership" ] @@ -4391,129 +4397,123 @@ { "group": "Resources", "pages": [ - "sdk/flutter/key-concepts", - "sdk/flutter/message-structure-and-hierarchy", - "sdk/flutter/real-time-listeners", - "sdk/flutter/rate-limits", - "sdk/flutter/connection-status", - "sdk/flutter/connection-behaviour" + "sdk/flutter/resources-overview", + "sdk/flutter/real-time-listeners" ] }, { - "group": "Reference", + "group": "Advanced", "pages": [ - "sdk/reference/messages", - "sdk/reference/entities", - "sdk/reference/auxiliary", - "sdk/reference/calls" + "sdk/flutter/advanced-overview", + "sdk/flutter/connection-status", + "sdk/flutter/connection-behaviour" ] }, - "sdk/flutter/upgrading-from-v3-guide", - "sdk/flutter/best-practices", - "sdk/flutter/error-codes", - "sdk/flutter/troubleshooting", + "sdk/flutter/upgrading-from-v4-guide", "sdk/flutter/extensions-overview", "sdk/flutter/ai-user-copilot-overview", "sdk/flutter/ai-chatbots-overview", "sdk/flutter/webhooks-overview", + "sdk/flutter/flutter-overview", "sdk/flutter/changelog" ] } ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", "pages": [ - "sdk/flutter/v5/overview", - "sdk/flutter/v5/setup", - { - "group": "Authentication", - "pages": [ - "sdk/flutter/v5/authentication-overview", - "sdk/flutter/v5/login-listeners" - ] - }, + "sdk/flutter/v4/overview", + "sdk/flutter/v4/setup", + "sdk/flutter/v4/authentication-overview", { "group": "Messaging", "pages": [ - "sdk/flutter/v5/messaging-overview", - "sdk/flutter/v5/send-message", - "sdk/flutter/v5/receive-messages", - "sdk/flutter/v5/additional-message-filtering", - "sdk/flutter/v5/retrieve-conversations", - "sdk/flutter/v5/edit-message", - "sdk/flutter/v5/flag-message", - "sdk/flutter/v5/delete-message", - "sdk/flutter/v5/delete-conversation", - "sdk/flutter/v5/typing-indicators", - "sdk/flutter/v5/interactive-messages", - "sdk/flutter/v5/transient-messages", - "sdk/flutter/v5/delivery-read-receipts", - "sdk/flutter/v5/threaded-messages", - "sdk/flutter/v5/mentions", - "sdk/flutter/v5/reactions" - ] - }, + "sdk/flutter/v4/send-message", + "sdk/flutter/v4/receive-messages", + "sdk/flutter/v4/additional-message-filtering", + "sdk/flutter/v4/retrieve-conversations", + "sdk/flutter/v4/edit-message", + "sdk/flutter/v4/flag-message", + "sdk/flutter/v4/delete-message", + "sdk/flutter/v4/delete-conversation", + "sdk/flutter/v4/typing-indicators", + "sdk/flutter/v4/interactive-messages", + "sdk/flutter/v4/transient-messages", + "sdk/flutter/v4/delivery-read-receipts", + "sdk/flutter/v4/threaded-messages", + "sdk/flutter/v4/mentions", + "sdk/flutter/v4/reactions" + ] + }, + "sdk/flutter/v4/calling-overview", { "group": "Users", "pages": [ - "sdk/flutter/v5/users-overview", - "sdk/flutter/v5/retrieve-users", - "sdk/flutter/v5/user-management", - "sdk/flutter/v5/block-users" + "sdk/flutter/v4/users-overview", + "sdk/flutter/v4/retrieve-users", + "sdk/flutter/v4/user-management", + "sdk/flutter/v4/block-users" ] }, - "sdk/flutter/v5/user-presence", + "sdk/flutter/v4/user-presence", { "group": "Groups", "pages": [ - "sdk/flutter/v5/groups-overview", - "sdk/flutter/v5/retrieve-groups", - "sdk/flutter/v5/create-group", - "sdk/flutter/v5/update-group", - "sdk/flutter/v5/join-group", - "sdk/flutter/v5/leave-group", - "sdk/flutter/v5/delete-group", - "sdk/flutter/v5/retrieve-group-members", - "sdk/flutter/v5/group-add-members", - "sdk/flutter/v5/group-kick-member", - "sdk/flutter/v5/group-change-member-scope", - "sdk/flutter/v5/transfer-group-ownership" + "sdk/flutter/v4/groups-overview", + "sdk/flutter/v4/retrieve-groups", + "sdk/flutter/v4/create-group", + "sdk/flutter/v4/update-group", + "sdk/flutter/v4/join-group", + "sdk/flutter/v4/leave-group", + "sdk/flutter/v4/delete-group", + "sdk/flutter/v4/retrieve-group-members", + "sdk/flutter/v4/group-add-members", + "sdk/flutter/v4/group-kick-ban-members", + "sdk/flutter/v4/group-change-member-scope", + "sdk/flutter/v4/transfer-group-ownership" ] }, - "sdk/flutter/v5/ai-moderation", - "sdk/flutter/v5/ai-agents", + "sdk/flutter/v4/ai-moderation", + "sdk/flutter/v4/ai-agents", { "group": "Resources", "pages": [ - "sdk/flutter/v5/resources-overview", - "sdk/flutter/v5/real-time-listeners" + "sdk/flutter/v4/key-concepts", + "sdk/flutter/v4/message-structure-and-hierarchy", + "sdk/flutter/v4/real-time-listeners", + "sdk/flutter/v4/rate-limits", + "sdk/flutter/v4/connection-status", + "sdk/flutter/v4/connection-behaviour" ] }, { - "group": "Advanced", + "group": "Reference", "pages": [ - "sdk/flutter/v5/advanced-overview", - "sdk/flutter/v5/connection-status", - "sdk/flutter/v5/connection-behaviour" + "sdk/reference/messages", + "sdk/reference/entities", + "sdk/reference/auxiliary", + "sdk/reference/calls" ] }, - "sdk/flutter/v5/upgrading-from-v4-guide", - "sdk/flutter/v5/extensions-overview", - "sdk/flutter/v5/ai-user-copilot-overview", - "sdk/flutter/v5/ai-chatbots-overview", - "sdk/flutter/v5/webhooks-overview", - "sdk/flutter/v5/flutter-overview", - "sdk/flutter/v5/changelog" + "sdk/flutter/v4/upgrading-from-v3-guide", + "sdk/flutter/v4/best-practices", + "sdk/flutter/v4/error-codes", + "sdk/flutter/v4/troubleshooting", + "sdk/flutter/v4/extensions-overview", + "sdk/flutter/v4/ai-user-copilot-overview", + "sdk/flutter/v4/ai-chatbots-overview", + "sdk/flutter/v4/webhooks-overview", + "sdk/flutter/v4/changelog" ] } ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5118,7 +5118,7 @@ "icon": "/images/icons/js.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5160,7 +5160,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5244,7 +5244,7 @@ "icon": "/images/icons/react.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5285,7 +5285,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5359,7 +5359,7 @@ "icon": "/images/icons/swift.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5400,7 +5400,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5474,7 +5474,7 @@ "icon": "/images/icons/android.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5514,7 +5514,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5588,7 +5588,7 @@ "icon": "/images/icons/flutter.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5628,7 +5628,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -9655,7 +9655,7 @@ "metatags": { "charset": "UTF-8", "viewport": "width=device-width, initial-scale=1.0", - "description": "Learn how to integrate, customize, and scale real-time chat using CometChat\u2019s UI Kits, SDKs, and widgets across popular frameworks. Get step-by-step guides, best practices, and implementation details to build production-ready chat experiences.", + "description": "Learn how to integrate, customize, and scale real-time chat using CometChat’s UI Kits, SDKs, and widgets across popular frameworks. Get step-by-step guides, best practices, and implementation details to build production-ready chat experiences.", "language": "en" } }, diff --git a/sdk/flutter/additional-message-filtering.mdx b/sdk/flutter/additional-message-filtering.mdx index 35f03b472..1d9687d68 100644 --- a/sdk/flutter/additional-message-filtering.mdx +++ b/sdk/flutter/additional-message-filtering.mdx @@ -1,95 +1,35 @@ --- title: "Additional Message Filtering" -sidebarTitle: "Message Filtering" -description: "Advanced filtering options for fetching messages using MessagesRequestBuilder in the CometChat Flutter SDK." --- - -```dart -// Basic message request for user conversation -MessagesRequest request = (MessagesRequestBuilder() - ..uid = "user_uid" - ..limit = 30 -).build(); - -// Fetch messages for group with filters -MessagesRequest request = (MessagesRequestBuilder() - ..guid = "group_guid" - ..limit = 50 - ..categories = ["message", "custom"] - ..types = ["text", "image"] - ..hideReplies = true -).build(); - -// Unread messages only -MessagesRequest request = (MessagesRequestBuilder() - ..uid = "user_uid" - ..unread = true - ..limit = 50 -).build(); - -// Paginate through messages -List messages = await request.fetchPrevious(); -List moreMessages = await request.fetchPrevious(); // Next page -``` -**Key properties:** `uid`, `guid`, `limit`, `categories`, `types`, `tags`, `unread`, `parentMessageId`, `messageId`, `timestamp`, `hideReplies`, `hideDeleted`, `hideQuotedMessages`, `searchKeyword`, `updatedAfter`, `updatesOnly`, `hideMessagesFromBlockedUsers`, `withTags`, `hasMentions`, `hasLinks`, `hasAttachments`, `hasReactions`, `mentionedUids`, `attachmentTypes`, `withParent` - - -The `MessagesRequest` class helps you fetch messages based on various parameters — returning [`BaseMessage`](/sdk/reference/messages#basemessage) objects that can be [`TextMessage`](/sdk/reference/messages#textmessage), [`MediaMessage`](/sdk/reference/messages#mediamessage), [`CustomMessage`](/sdk/reference/messages#custommessage), [`Action`](/sdk/reference/messages#action), or [`Call`](/sdk/reference/messages#call). It uses the Builder design pattern via `MessagesRequestBuilder`. - -To fetch messages: -1. Create a `MessagesRequestBuilder` object -2. Set your desired parameters -3. Call `build()` to get a `MessagesRequest` object -4. Call `fetchNext()` or `fetchPrevious()` to retrieve messages - -| Method | Description | -| --- | --- | -| `fetchNext()` | Returns messages after the specified parameters | -| `fetchPrevious()` | Returns messages before the specified parameters | - -Messages are paginated with a maximum of 100 per request. Calling `fetchPrevious()`/`fetchNext()` repeatedly on the same object gets subsequent pages. - -### MessagesRequestBuilder Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| `uid` | `String?` | User ID to fetch conversation messages for | -| `guid` | `String?` | Group ID to fetch conversation messages for | -| `limit` | `int?` | Number of messages per request (max 100, default 30) | -| `messageId` | `int?` | Fetch messages before/after this message ID | -| `timestamp` | `DateTime?` | Fetch messages before/after this timestamp | -| `unread` | `bool?` | Fetch only unread messages | -| `hideMessagesFromBlockedUsers` | `bool?` | Exclude messages from blocked users (default `false`) | -| `searchKeyword` | `String?` | Search keyword to filter messages | -| `updatedAfter` | `DateTime?` | Fetch messages updated/received after this time | -| `updatesOnly` | `bool?` | Fetch only updated messages (use with `updatedAfter`) | -| `category` | `String?` | Single message category filter | -| `type` | `String?` | Single message type filter | -| `parentMessageId` | `int?` | Fetch messages in a specific thread | -| `hideReplies` | `bool?` | Exclude threaded messages (default `false`) | -| `hideDeleted` | `bool?` | Exclude deleted messages (default `false`) | -| `categories` | `List?` | Filter by multiple message categories | -| `types` | `List?` | Filter by multiple message types | -| `withTags` | `bool?` | Include tag information in response (default `false`) | -| `tags` | `List?` | Filter by specific tags | -| `hasMentions` | `bool?` | Fetch only messages with mentions (default `false`) | -| `hasLinks` | `bool?` | Fetch only messages with links (default `false`) | -| `hasAttachments` | `bool?` | Fetch only messages with attachments (default `false`) | -| `hasReactions` | `bool?` | Fetch only messages with reactions (default `false`) | -| `mentionedUids` | `List?` | Fetch messages mentioning specific users | -| `attachmentTypes` | `List?` | Filter by specific attachment types | -| `interactionGoalCompletedOnly` | `bool?` | Fetch only messages with completed interaction goals (default `false`) | -| `withParent` | `bool?` | Include parent message with replies (default `false`) | -| `hideQuotedMessages` | `bool?` | Exclude quoted messages (default `false`) | +The `MessagesRequest` class as you must be familiar with helps you to fetch messages based on the various parameters provided to it. This document will help you understand better the various options that are available using the `MessagesRequest` class. + +The `MessagesRequest` class is designed using the `Builder design pattern`. In order to obtain an object of the `MessagesRequest` class, you will have to make use of the `MessagesRequestBuilder` class in the `MessagesRequest` class. + +The `MessagesRequestBuilder` class allows you to set various parameters to the `MessagesRequest` class based on which the messages are fetched. + +Steps to generate an object of the MessagesRequest class: + +1. Create an object of the `MessagesRequestBuilder` class. +2. Set all the parameters you wish to set. +3. Call the `build()` method of the `MessagesRequestBuilder` class to get an object of the `MessagesRequest` class. + +Once you have an object of the `MessagesRequest` class, you can call either the `fetchNext()` method or the `fetchPrevious()` method using the object. + +1. fetchNext() - Calling this method will return the messages after the specified parameters. +2. fetchPrevious() - Calling this method will give you messages before the specified parameters. + +Since messages are obtained in a paginated manner, a `maximum of 100` messages can be pulled in a single iteration. Calling the `fetchPrevious()`/`fetchNext()` method on the same `MessagesRequest` object will get you the next set of messages. + +Now that you are clear how to use the `MessagesRequest` class, below are the various options available: ## Number of messages fetched *In other words, how do I set the number of messages fetched in a single iteration* -To achieve this, you can use the `limit` property. This takes an integer value and informs the SDK to fetch the specified number of messages in one iteration. The maximum number of messages that can be fetched in one go is `100`. +To achieve this, you can use the `setLimit()` method. This method takes an integer value as the input and informs the SDK to fetch the specified number of messages in one iteration. The maximum number of messages that can be fetched in one go is `100`. The default limit is `30`. @@ -105,11 +45,32 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() +## Direct Page Navigation + +*In other words, how do I jump to a specific page of messages* + +You can use the `setPage()` method to directly navigate to a specific page of messages. This enables direct page navigation and improved data handling in paginated responses. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..limit = 30 + ..page = 2).build(); +``` + + + + + ## Messages for a user conversation *In other words, how do I fetch messages between me and any user* -Use the `uid` property to fetch messages between the logged-in user and a specific user. +This can be achieved using the `UID` parameter. This method takes the UID of the user with whom the conversation is to be fetched. When a valid UID is passed, the SDK will return all the messages that are a part of the conversation between the logged-in user and the UID that has been specified. @@ -128,7 +89,7 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() *In other words, how do I fetch messages for any group conversation* -Use the `guid` property to fetch messages from a group. The logged-in user must be a member of the group. +You can achieve this using the `GUID` method. This method takes the GUID of a group for which the conversations are to be fetched. Passing a valid GUID to this method will return all the messages that are a part of the group conversation. Please note that the logged-in user must be a member of the group to fetch the messages for that group. @@ -143,15 +104,11 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() - -If neither `uid` nor `guid` is set, all messages for the logged-in user across all conversations will be fetched. All parameters below can be combined with `uid` or `guid`. - - ## Messages before/after a message *In other words, how do I fetch messages before or after a particular message* -Use the `messageId` property. This provides messages only after/before the message-id based on if `fetchNext()` or `fetchPrevious()` is called. +This can be achieved using the `messageId` parameter. This method takes the messageId as input and provides messages only after/before the message-id based on if the fetchNext() or fetchPrevious() method is triggered. @@ -168,13 +125,13 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -This property can be used along with `uid` or `guid` to fetch messages after/before any specific message-id for a particular user/group conversation. +This method can be used along with `UID` or `GUID` parameter to fetch messages after/before any specific message-id for a particular user/group conversation. ## Messages before/after a given time *In other words, how do I fetch messages before or after a particular date or time* -Use the `timestamp` property. This takes a `DateTime` value and provides messages only after/before the timestamp based on if `fetchNext()` or `fetchPrevious()` is called. +You can easily achieve this using the `timestamp` parameter . This method takes the `DateTime` timestamp as input and provides messages only after/before the timestamp based on if fetchNext() or fetchPrevious() method is triggered. @@ -191,13 +148,13 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -This property can be used along with `uid` or `guid` to fetch messages after/before any specific date or time for a particular user/group conversation. +This method can be used along with `UID` or `UID` methods to fetch messages after/before any specific date or time for a particular user/group conversation. ## Unread messages *In other words, how do I fetch unread messages* -Use the `unread` property set to `true` to return just the unread messages. +This can easily be achieved using setting the unread flag to true. For this, you need to use the unread parameter. This method takes a boolean value as input. If the value is set to true, the SDK will return just the unread messages. @@ -214,13 +171,13 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -Combine with `guid` or `uid` to fetch unread messages for a specific conversation. +This method along with `GUID` or `UID` can be used to fetch unread messages for a particular group or user conversation respectively. ## Exclude messages from blocked users *In other words, how do I fetch messages excluding the messages from the users I have blocked* -Use the `hideMessagesFromBlockedUsers` property. If set to `true`, messages from users blocked by the logged-in user will be excluded. Default is `false`. +This can be easily achieved using the `hideMessagesFromBlockedUsers` parameter. This method accepts a boolean value which determines if the messages from users blocked by the logged-in user need to be a part if the fetched messages. If the value is set to true, the messages will be hidden and won't be a part of the messages fetched. The default value is false i.e if this method is not used, the messages from blocked users will be included in the fetched messages. @@ -237,13 +194,13 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -This also works in group conversations where both users are members. +This parameter can be used to hide the messages by users blocked by logged in user in groups that both the members are a part of. ## Updated and received messages *In other words, how do I fetch messages that have been received or updated after a particular date or time* -Use the `updatedAfter` property with a `DateTime` value to return all messages that have been updated and the ones that have been sent/received after the specified time. Updated messages include those marked as read/delivered, edited, or deleted. +This method accepts a `DateTime` timestamp value and will return all the messages that have been updated and the ones that have been sent/received after the specified time. The messages updated could mean the messages that have been marked as read/delivered or if the messages are edited or deleted. @@ -260,13 +217,13 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -Useful for syncing messages with a local database — fetch only what's changed since your last sync. +This can be useful in finding the messages that have been received or updated after a certain time. It can prove very useful if you are saving the messages locally and would like to know if the messages that have been updated or received after the last message is available in your local databases. ## Updated messages only *In other words, how do I fetch messages that have been updated after a particular date or time* -Use the `updatesOnly` property set to `true` together with `updatedAfter` to get just the updated messages and not the messages sent/received after the specified time. This property must be used with `updatedAfter`. +This can be achieved easily by setting the updatesOnly parameter to true. To do so, you can use the updatesOnly() method. This method takes a boolean input and can be used with the `updatedAfter` parameter to get just the updated messages and not the messages sent/received after the specified time. This method cannot be used independently and always needs to be used with the `updatedAfter` parameter. @@ -288,9 +245,9 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() *In other words, how do I fetch messages belonging to multiple categories* -We recommend before trying this, you refer to the [Message structure and hierarchy guide](/sdk/flutter/message-structure-and-hierarchy) to get familiar with the various categories of messages. +We recommend before trying this, you refer to the [Message Structure and Hierarchy](/sdk/flutter/v5/messaging-overview#message-structure-and-hierarchy) guide to get familiar with the various categories of messages. -Use the `categories` property with a list of category names to filter by message category. +For this, you will have to use the `categories` method. This method accepts a list of categories. This tells the SDK to fetch messages only belonging to these categories. @@ -310,15 +267,15 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -The above snippet will help you get only the messages belonging to the `message` and `custom` category. This can also be used to disable certain categories of messages like `call` and `action`. This along with `uid` and `guid` can help display only the messages you wish to display avoiding the other category of messages. +The above snippet will help you get only the messages belonging to the `message` and `custom` category. This can also be used to disable certain categories of messages like `call` and `action`. This along with `UID` and GUID can help display only the messages you wish to display avoiding the other category of messages. ## Messages for multiple types *In other words, how do I fetch messages belonging to multiple types* -We recommend you refer to the [Message structure and hierarchy guide](/sdk/flutter/message-structure-and-hierarchy) to get familiar with the various types of messages before trying this out. +We recommend you refer to the [Message Structure and Hierarchy](/sdk/flutter/v5/messaging-overview#message-structure-and-hierarchy) guide to get familiar with the various types of messages before trying this out. -Use the `types` property with a list of type names to filter by message type. +This can be easily achieved using the `types` parameter, which accepts a list of types. This tells the SDK to fetch messages only belonging to these types. @@ -338,13 +295,13 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -Using the above code snippet, you can fetch all the media messages. This along with `uid` or `guid` can be used to fetch media messages for any particular conversation. This can be useful in many other scenarios as well. +Using the above code snippet, you can fetch all the media messages. This along with UID or GUID can be used to fetch media messages for any particular conversation. This can be useful in many other scenarios as well. ## Messages for a specific thread *In other words, how do I fetch messages that are a part of a thread and not directly a user/group conversations* -Use the `parentMessageId` property when you have implemented threaded conversations. This will return only messages belonging to the thread with the specified parent ID. +This can be done using the `parentMessageId` parameter. This parameter needs to be used when you have implemented threaded conversations in your app. This method will return the messages only belonging to the thread with the specified parent Id. @@ -367,7 +324,7 @@ The above code snippet returns the messages that belong to the thread with paren *In other words, how do I exclude threaded messages from the normal user/group conversations* -Use the `hideReplies` property set to `true` to exclude messages that belong to threads. Default is `false`. +In order to do this, you can use the `hideReplies` parameter. This parameter is also related to threaded conversations. This method takes boolean as input. This boolean when set to true will make sure that the messages that belong to threads are not fetched. If set to false, which is also the default value, the messages belong to the threads will also be fetched along with other messages. @@ -388,7 +345,7 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() *In other words, how do I exclude deleted messages a user/group conversations* -Use the `hideDeleted` property set to `true` to exclude deleted messages. Default is `false`. +In order to do this, you can use the hideDeleted parameter. This parameter takes boolean as input. If set to true, it will make sure that the deleted messages are not fetched. If set to false, which is also the default value, the deleted messages will also be fetched along with other messages. @@ -405,32 +362,11 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -## Hide quoted messages in user/group conversations - -*In other words, how do I exclude quoted messages from user/group conversations* - -Use the `hideQuotedMessages` property set to `true` to exclude quoted messages. Default is `false`. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hideQuotedMessages = true - ..limit = 50).build(); -``` - - - - - ## Messages by tags *In other words, how do I fetch messages belonging to specific tags* -Use the `tags` property with a list of tag names to fetch only messages with those tags. +In order to do this, you can use the `tags` parameter. This parameter accepts a list of tags. This tells the SDK to fetch messages only belonging to these tags. @@ -454,7 +390,7 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() *In other words, how do I fetch messages with the tags information* -Use the `withTags` property set to `true` to include tag information in the response. Default is `false`. +In order to do this, you can use the `withTags` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages along with the tags information. When set to `false` , the SDK will not fetch tags information associated with messages. The default value for this parameter is `false` . @@ -463,7 +399,7 @@ String UID = "cometchat-uid-1"; MessagesRequest messageRequest = (MessagesRequestBuilder() ..uid = UID - ..withTags = true + ...withTags = true ..limit = 50).build(); ``` @@ -471,17 +407,11 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -When `withTags` is set to `true`, each message's tags field will be populated. - -| Additional Field | Type | Description | -| --- | --- | --- | -| tags | `List` | Tags associated with the message | - ## Messages with links -*In other words, as a logged-in user, how do I fetch messages that contain links?* +In other words, as a logged-in user, how do I fetch messages that contains links? -Use the `hasLinks` property set to `true` to fetch only messages containing links. Default is `false`. +In order to do this, you can use the `hasLinks` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages which have links in the text. The default value for this parameter is `false`. @@ -506,9 +436,9 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() ## Messages with attachments -*In other words, as a logged-in user, how do I fetch messages that contain attachments?* +In other words, as a logged-in user, how do I fetch messages that contains attachments? -Use the `hasAttachments` property set to `true` to fetch only messages with attachments (image, audio, video, or file). Default is `false`. +In order to do this, you can use the `hasAttachments` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages which have attachments (image, audio, video or file). The default value for this parameter is `false`. @@ -533,9 +463,9 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() ## Messages with reactions -*In other words, as a logged-in user, how do I fetch messages that contain reactions?* +In other words, as a logged-in user, how do I fetch messages that contains reactions? -Use the `hasReactions` property set to `true` to fetch only messages with reactions. Default is `false`. +In order to do this, you can use the `hasReactions` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages which have reactions. The default value for this parameter is `false`. @@ -560,9 +490,9 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() ## Messages with mentions -*In other words, as a logged-in user, how do I fetch messages that contain mentions?* +In other words, as a logged-in user, how do I fetch messages that contains mentions? -Use the `hasMentions` property set to `true` to fetch only messages with mentions. Default is `false`. +In order to do this, you can use the `hasMentions` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages which have mentions. The default value for this parameter is `false`. @@ -587,9 +517,9 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() ## Messages with particular user mentions -*In other words, as a logged-in user, how do I fetch messages that mention specific users?* +In other words, as a logged-in user, how do I fetch messages that mentions specific users? -Use the `mentionedUids` property with a list of UIDs to fetch messages that mention specific users. +In order to do this, you can use the `mentionedUids` parameter. This parameter accepts a list of UIDs. When set, the SDK will fetch messages which have the mentions of the UIDs passed. @@ -602,7 +532,7 @@ This feature is only available with `Conversation & Advanced Search`. The `Conve ```dart String UID = "cometchat-uid-1"; List mentionedUIDs = []; -mentionedUIDs.add("cometchat-uid-1"); +tags.add("cometchat-uid-1"); MessagesRequest messageRequest = (MessagesRequestBuilder() ..uid = UID ..mentionedUids = mentionedUIDs @@ -613,17 +543,42 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() -## Messages with specific attachment types -*In other words, as a logged-in user, how do I fetch messages with specific types of attachments?* +## Messages by attachment types -Use the `attachmentTypes` property with a list of attachment type values to fetch messages with specific attachment types. +*In other words, how do I fetch messages with specific attachment types* - +In order to do this, you can use the `attachmentTypes` parameter. This parameter accepts a list of attachment types. This tells the SDK to fetch messages only with the specified attachment types. -This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + +```dart +String UID = "cometchat-uid-1"; +List attachmentTypes = []; +attachmentTypes.add("image"); +attachmentTypes.add("video"); - +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..attachmentTypes = attachmentTypes + ..limit = 50).build(); +``` + + + + + +The available attachment types are: +- `image` - Image attachments +- `video` - Video attachments +- `audio` - Audio attachments +- `file` - File/document attachments + +## Hide quoted messages + +*In other words, how do I exclude quoted messages from the response* + +In order to do this, you can use the `hideQuotedMessages` parameter. This parameter accepts boolean as input. When set to `true`, the SDK will not include the quoted message object in the response. The default value for this parameter is `false`. @@ -632,7 +587,7 @@ String UID = "cometchat-uid-1"; MessagesRequest messageRequest = (MessagesRequestBuilder() ..uid = UID - ..attachmentTypes = ["image", "video"] + ..hideQuotedMessages = true ..limit = 50).build(); ``` @@ -640,21 +595,4 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() ---- - -## Next Steps - - - - Handle incoming messages in real-time with listeners - - - Fetch and display conversation lists with filtering options - - - Understand message categories, types, and hierarchy - - - Work with message threads and replies - - +This can be useful when you want to reduce the payload size and don't need the full quoted message details in the response. diff --git a/sdk/flutter/v5/advanced-overview.mdx b/sdk/flutter/advanced-overview.mdx similarity index 100% rename from sdk/flutter/v5/advanced-overview.mdx rename to sdk/flutter/advanced-overview.mdx diff --git a/sdk/flutter/ai-agents.mdx b/sdk/flutter/ai-agents.mdx index 173304cf6..1ee853823 100644 --- a/sdk/flutter/ai-agents.mdx +++ b/sdk/flutter/ai-agents.mdx @@ -1,98 +1,40 @@ --- title: "AI Agents" -sidebarTitle: "AI Agents" -description: "Learn how to integrate AI Agents in your Flutter app to enable intelligent, automated interactions that process user messages, trigger tools, and respond with contextually relevant information." --- - - -| Feature | Description | -| --- | --- | -| [AI Agents](#agent-run-lifecycle-and-message-flow) | Intelligent automated conversations with real-time streaming | -| [AI Moderation](/sdk/flutter/ai-moderation) | Automatic content moderation with `PENDING` → `APPROVED` / `DISAPPROVED` flow | -| [AI User Copilot](/fundamentals/ai-user-copilot/overview) | Smart Replies, Conversation Starter, Conversation Summary (Dashboard-enabled) | - -```dart -// Add AI Assistant listener for real-time events -CometChat.addAIAssistantListener("LISTENER_ID", AIAssistantListener( - onAIAssistantEventReceived: (AIAssistantBaseEvent event) { - debugPrint("AI Event: ${event.type}"); - }, -)); - -// Add Message listener for agentic messages -CometChat.addMessageListener("LISTENER_ID", MessageListener( - onAIAssistantMessageReceived: (AIAssistantMessage msg) { - debugPrint("AI Reply: ${msg.text}"); - }, - onAIToolResultReceived: (AIToolResultMessage result) { - debugPrint("Tool Result: $result"); - }, -)); - -// Remove listeners when done -CometChat.removeAIAssistantListener("LISTENER_ID"); -CometChat.removeMessageListener("LISTENER_ID"); -``` - -**Prerequisites:** `CometChat.init()` + `CometChat.login()` completed, AI features enabled in [Dashboard](https://app.cometchat.com) -**Event flow:** Run Start → Tool Call(s) → Text Message Stream → Run Finished - - -AI Agents enable intelligent, automated interactions within your application. They process user messages, trigger tools, and respond with contextually relevant information. For a broader introduction, see the [AI Agents section](/ai-agents). - - -Agents only respond to text messages. - +# AI Agents Overview + +AI Agents enable intelligent, automated interactions within your application. They can process user messages, trigger tools, and respond with contextually relevant information. For a broader introduction, see the [AI Agents section](/ai-agents). + +> **Note:** +> Currently, an Agent only responds to **Text Messages**. ## Agent Run Lifecycle and Message Flow -When a user sends a text message to an Agent: -1. The platform starts a run and streams real-time events via `AIAssistantListener` -2. After the run completes, persisted Agentic Messages arrive via `MessageListener` +This section explains how a user’s text message to an Agent becomes a structured "run" which emits real-time events and then produces agentic messages for historical retrieval. +- A user sends a text message to an Agent. +- The platform starts a run and streams real-time events via the **`AIAssistantListener`**. +- After the run completes, persisted Agentic Messages arrive via the **`MessageListener`**. ### Real-time Events - -Events arrive via `onAIAssistantEventReceived` in this order: - -| Order | Event | Description | -|-------|-------|-------------| -| 1 | Run Start | A new run has begun | -| 2 | Tool Call Start | Agent decided to invoke a tool | -| 3 | Tool Call Arguments | Arguments being passed to the tool | -| 4 | Tool Call End | Tool execution completed | -| 5 | Tool Call Result | Tool's output is available | -| 6 | Text Message Start | Agent started composing a reply | -| 7 | Text Message Content | Streaming content chunks (multiple) | -| 8 | Text Message End | Agent reply is complete | -| 9 | Run Finished | Run finalized; persisted messages follow | - - -`Run Start` and `Run Finished` are always emitted. Tool Call events only appear when tools are invoked — there can be multiple tool call cycles in a single run. Text Message events are always emitted and carry the assistant's reply incrementally. - - -### Event Object Properties - -Every event is an [`AIAssistantBaseEvent`](/sdk/reference/messages#aiassistantbaseevent) with these common properties: - -| Getter | Return Type | Description | -|--------|-------------|-------------| -| `type` | `String` | Event type (e.g., `run_started`, `text_message_content`) | -| `conversationId` | `String` | The conversation this event belongs to | -| `messageId` | `String` | The message ID associated with the event | -| `parentMessageId` | `String` | Parent message ID (for threaded messages) | -| `runId` | `String` | The run ID for this agent execution | -| `threadId` | `String` | The thread ID for this agent execution | -| `timestamp` | `int` | Timestamp of the event | -| `data` | `Map` | Full event data payload | - -Some events carry additional data: - -| Event | Extra Property | Description | -|-------|---------------|-------------| -| Text Message Content | `delta` | The streaming text chunk for progressive rendering | -| Tool Call Arguments | `toolCallId`, `delta` | Tool call ID and argument chunk | -| Tool Call Result | `toolCallId`, `content`, `role` | Tool call ID, result content, and role | +Events are received via the **`onAIAssistantEventReceived`** method of the **`AIAssistantListener`** class in this general order: + +1. Run Start +2. Zero or more tool call cycles (repeats for each tool invocation): + - Tool Call Start + - Tool Call Arguments + - Tool Call End + - Tool Call Result +3. One or more assistant reply streams: + - Text Message Start + - Text Message Content (multiple times; token/char streaming) + - Text Message End +4. Run Finished + +Notes: +- `Run Start` and `Run Finished` are always emitted. +- `Tool Call` events appear only when a backend or frontend tool is invoked. There can be multiple tool calls in a single run. +- `Text Message` events are always emitted and carry the assistant’s reply incrementally. @@ -126,16 +68,12 @@ class AIAssistantEventHandler with AIAssistantListener { - -Always remove AI Assistant listeners when they're no longer needed (e.g., on widget dispose or page navigation). Failing to remove listeners can cause memory leaks and duplicate event handling. - - #### Event descriptions -- Run Start: A new run has begun for the user's message. +- Run Start: A new run has begun for the user’s message. - Tool Call Start: The agent decided to invoke a tool. - Tool Call Arguments: Arguments being passed to the tool. - Tool Call End: Tool execution completed. -- Tool Call Result: Tool's output is available. +- Tool Call Result: Tool’s output is available. - Text Message Start: The agent started composing a reply. - Text Message Content: Streaming content chunks for progressive rendering. - Text Message End: The agent reply is complete. @@ -143,31 +81,10 @@ Always remove AI Assistant listeners when they're no longer needed (e.g., on wid ### Agentic Messages -After the run completes, these messages arrive via `MessageListener`: - -| Message Type | Description | -|--------------|-------------| -| `AIAssistantMessage` | The full assistant reply | -| `AIToolResultMessage` | The final output of a tool call | -| `AIToolArgumentMessage` | The arguments passed to a tool | - -Each message type extends `BaseMessage` and has a typed data accessor: - -| Message Type | Data Getter | Data Properties | -|--------------|-------------|-----------------| -| `AIAssistantMessage` | `getAssistantMessageData()` | `runId`, `threadId`, `text` | -| `AIToolResultMessage` | `getToolResultMessageData()` | `runId`, `threadId`, `text`, `toolCallId` | -| `AIToolArgumentMessage` | `getToolArgumentMessageData()` | `runId`, `threadId`, `toolCalls` | - -The `toolCalls` on `AIToolArgumentMessage` returns a list of `AIToolCall` objects, each with: - -| Property | Type | Description | -|----------|------|-------------| -| `id` | `String` | Unique tool call ID | -| `type` | `String` | Tool call type | -| `function` | `AIToolCallFunction` | Function object with `name` and `arguments` | -| `displayName` | `String` | Display name of the tool | -| `executionText` | `String` | Execution description text | +These events are received via the **`MessageListener`** after the run completes. +- `AIAssistantMessage`: The full assistant reply. +- `AIToolResultMessage`: The final output of a tool call. +- `AIToolArgumentMessage`: The arguments that were passed to a tool. @@ -194,23 +111,4 @@ The `toolCalls` on `AIToolArgumentMessage` returns a list of `AIToolCall` object } ``` - - ---- - -## Next Steps - - - - Configure and deploy AI chatbots for automated conversations - - - Implement AI-powered content moderation for your chat - - - AI-powered features like smart replies and conversation summaries - - - Send text messages that trigger AI Agent responses - - + \ No newline at end of file diff --git a/sdk/flutter/ai-chatbots-overview.mdx b/sdk/flutter/ai-chatbots-overview.mdx index f3037d7e9..7a460169b 100644 --- a/sdk/flutter/ai-chatbots-overview.mdx +++ b/sdk/flutter/ai-chatbots-overview.mdx @@ -1,65 +1,4 @@ --- title: "Bots" -sidebarTitle: "AI Bots" -description: "Configure AI-powered chatbots to provide automated assistance and maintain conversational momentum in your Flutter app." url: "/ai-chatbots/overview" --- - - - -```dart -// AI Bots are configured via the CometChat Dashboard -// Navigate to: AI Agents > Custom Bots > AI Bots - -// Configuration options: -// 1. Set GPT Model (e.g., gpt-4, gpt-3.5-turbo) -// 2. Add your OpenAI API Key -// 3. Set Custom Instructions for bot behavior -// 4. Configure Temperature (0-1) for response creativity -// 5. Enable AI toggle - -// Once configured, bots respond automatically to user messages -// No additional SDK code required - bots work via CometChat backend -``` - -**Dashboard Path:** [CometChat Dashboard](https://app.cometchat.com) → Your App → AI Agents → Custom Bots → AI Bots - - - -AI Bots provide automated assistance to users seeking guidance or insights. Configure intelligent chatbots through the CometChat Dashboard to maintain conversational momentum in your Flutter application. - - - **Available via:** [Dashboard](https://app.cometchat.com) | [REST - API](https://api-explorer.cometchat.com) | UI Kits - - ---- - -## Next Steps - - - - Build custom AI agents that respond to user queries - - - Automatically moderate content using AI - - - Enhance user experience with AI-powered assistance - - - Learn how to send messages that bots can respond to - - diff --git a/sdk/flutter/ai-moderation.mdx b/sdk/flutter/ai-moderation.mdx index 4d8868826..e0414eb82 100644 --- a/sdk/flutter/ai-moderation.mdx +++ b/sdk/flutter/ai-moderation.mdx @@ -1,42 +1,11 @@ --- title: "AI Moderation" -sidebarTitle: "AI Moderation" -description: "Learn how to implement AI-powered content moderation in your Flutter app using CometChat SDK to automatically review messages for inappropriate content." --- - - -```dart -// Send message and check moderation status -CometChat.sendMessage(textMessage, onSuccess: (TextMessage message) { - if (message.moderationStatus?.value == ModerationStatusEnum.PENDING.value) { - // Message is under moderation review - } -}, onError: (e) {}); - -// Listen for moderation results -CometChat.addMessageListener("MODERATION_LISTENER", MessageListener( - onMessageModerated: (BaseMessage message) { - // Handle APPROVED or DISAPPROVED status - }, -)); - -// Remove listener when done -CometChat.removeMessageListener("MODERATION_LISTENER"); -``` - -**Moderation Status:** `PENDING` → `APPROVED` or `DISAPPROVED` -**Supported Types:** Text, Image, Video messages - - ## Overview AI Moderation in the CometChat SDK helps ensure that your chat application remains safe and compliant by automatically reviewing messages for inappropriate content. This feature leverages AI to moderate messages in real-time, reducing manual intervention and improving user experience. - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) | [Dashboard](https://app.cometchat.com) - - For a broader understanding of moderation features, configuring rules, and managing flagged messages, see the [Moderation Overview](/moderation/overview). @@ -84,8 +53,6 @@ Moderation is triggered **only** for the following message types: | Text Messages | ✅ | Content analyzed for inappropriate text | | Image Messages | ✅ | Images scanned for unsafe content | | Video Messages | ✅ | Videos analyzed for prohibited content | - -Moderation applies to [`TextMessage`](/sdk/reference/messages#textmessage) and [`MediaMessage`](/sdk/reference/messages#mediamessage) types. | Custom Messages | ❌ | Not subject to AI moderation | | Action Messages | ❌ | Not subject to AI moderation | @@ -131,99 +98,6 @@ When you send a text, image, or video message, check the initial moderation stat - -**On Success** — A `TextMessage` object containing all details of the sent message, including the initial moderation status: - - - -**TextMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `501` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#send-moderated-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#send-moderated-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `text` | string | The text content of the message | `"Hello, how are you?"` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `moderationStatus` | string | Moderation status of the message | `"pending"` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_BLOCKED_BY_EXTENSION"` | -| `message` | string | Human-readable error message | `"Message blocked by moderation."` | -| `details` | string | Additional technical details | `"The message was flagged and blocked by the AI moderation extension."` | - - - ### Step 2: Listen for Moderation Results Implement the `MessageListener` to receive moderation results in real-time: @@ -298,20 +172,4 @@ When a message is disapproved, handle it appropriately in your UI: ## Next Steps - -After implementing AI Moderation, explore these related features: - - - - Build intelligent AI-powered agents for automated conversations - - - Allow users to manually report inappropriate messages - - - Create automated chatbot experiences for your users - - - Handle incoming messages and moderation events - - +After implementing AI Moderation, consider adding a reporting feature to allow users to flag messages they find inappropriate. For more details, see the [Flag Message](/sdk/flutter/flag-message) documentation. diff --git a/sdk/flutter/ai-user-copilot-overview.mdx b/sdk/flutter/ai-user-copilot-overview.mdx index 12601d050..3e798a3fb 100644 --- a/sdk/flutter/ai-user-copilot-overview.mdx +++ b/sdk/flutter/ai-user-copilot-overview.mdx @@ -1,6 +1,4 @@ --- title: "AI" -sidebarTitle: "AI User Copilot" -description: "Enhance your Flutter app with AI-powered features like Smart Replies, Conversation Starters, and Conversation Summaries using CometChat." url: "/fundamentals/ai-user-copilot/overview" ---- +--- \ No newline at end of file diff --git a/sdk/flutter/authentication-overview.mdx b/sdk/flutter/authentication-overview.mdx index 07ff5d486..87b335748 100644 --- a/sdk/flutter/authentication-overview.mdx +++ b/sdk/flutter/authentication-overview.mdx @@ -1,69 +1,52 @@ --- title: "Authentication" -sidebarTitle: "Authentication" -description: "Create users, log in with Auth Key or Auth Token, check login status, and log out using the CometChat Flutter SDK." +sidebarTitle: "Overview" --- -{/* TL;DR for Agents and Quick Reference */} - +To allow a user to use CometChat, the user must log in to CometChat. -```dart -// Check existing session -User? user = await CometChat.getLoggedInUser(); - -// Login with Auth Key (development only) -CometChat.login("cometchat-uid-1", "AUTH_KEY", - onSuccess: (User user) { debugPrint("Logged in: $user"); }, - onError: (CometChatException e) { debugPrint("Error: ${e.message}"); } -); - -// Login with Auth Token (production) -CometChat.loginWithAuthToken("AUTH_TOKEN", - onSuccess: (User user) { debugPrint("Logged in: $user"); }, - onError: (CometChatException e) { debugPrint("Error: ${e.message}"); } -); - -// Logout -CometChat.logout( - onSuccess: (String msg) { debugPrint("Logged out"); }, - onError: (CometChatException e) { debugPrint("Error: ${e.message}"); } -); -``` - -**Create users via:** [Dashboard](https://app.cometchat.com) (testing) | [REST API](https://api-explorer.cometchat.com/reference/creates-user) (production) -**Test UIDs:** `cometchat-uid-1` through `cometchat-uid-5` - +**CometChat does not handle user management.** You must handle user registration and login at your end. Once the user is logged into your app/site, you can log in the user to CometChat **programmatically**. The user does not ever directly login to CometChat. -After [initializing](/sdk/flutter/setup) the SDK, the next step is to authenticate your user. CometChat provides two login methods — Auth Key for quick development, and Auth Token for production — both accessed through the `login()` method. +**CometChat does not handle friends management.** If you want to associate friends with your users, you must handle friends management in your app. Once two users are friends, you can associate them as friends in CometChat. -### How It Works +### Typical Workflow ```mermaid sequenceDiagram - participant User - participant YourApp as Your App - participant YourServer as Your Server - participant CometChat as CometChat - - User->>YourApp: Signs up / Logs in - YourApp->>YourServer: Authenticate user - YourServer->>CometChat: Create user (REST API, first time only) - CometChat-->>YourServer: User created - YourServer->>CometChat: Create Auth Token (REST API) - CometChat-->>YourServer: Auth Token - YourServer-->>YourApp: Return Auth Token - YourApp->>CometChat: CometChat.loginWithAuthToken(authToken) - CometChat-->>YourApp: User object (logged in) + participant App as Your App + participant Server as Your Server + participant CC as CometChat + + Note over App,CC: Registration + App->>Server: User registers + Server->>Server: Store user in DB + Server->>CC: Create user via REST API + + Note over App,CC: Login + App->>Server: User logs in + Server->>Server: Verify credentials + Server->>CC: Generate Auth Token + Server-->>App: Return Auth Token + App->>CC: CometChat.login(uid, authToken) + CC-->>App: Session established + + Note over App,CC: Messaging + App->>CC: Send and receive messages ``` -## Before You Log In +| Your App | Your Server | CometChat | +|----------|-------------|-----------| +| User registers | Store user info in your database | Create user via REST API (ID & name) | +| User logs in | Verify credentials, retrieve user ID | Log in user programmatically | +| User sends a friend request | Display the request to the potential friend | No action required | +| User accepts a friend request | Display the users as friends | Add both users as friends via REST API | -### Create a User +## Create User -A user must exist in CometChat before they can log in. +Before you login the user, you must add the user to CometChat. -- **During development:** Create users from the [CometChat Dashboard](https://app.cometchat.com). Five test users are already available with UIDs `cometchat-uid-1` through `cometchat-uid-5`. -- **In production:** Call the [Create User REST API](https://api-explorer.cometchat.com/reference/creates-user) when a user signs up in your app. +1. **For proof of concept/MVPs**: Create the user using the [CometChat Dashboard](https://app.cometchat.com/). +2. **For production apps**: Use the CometChat [Create User API](https://api-explorer.cometchat.com/reference/creates-user) to create the user when your user signs up in your app. @@ -71,31 +54,19 @@ We have setup 5 users for testing having UIDs: `cometchat-uid-1`, `cometchat-uid -### Check for an Existing Session +Once initialization is successful, you will need to log the user into CometChat using the `login()` method. -The SDK persists the logged-in user's session locally. Before calling `login()`, always check whether a session already exists — this avoids unnecessary login calls and keeps your app responsive. +We recommend you call the CometChat `login()` method once your user logs into your app. The `login()` method needs to be called only once. -```dart -User? user = await CometChat.getLoggedInUser(); -if (user != null) { - // User is already logged in — proceed to your app -} -``` - -If `getLoggedInUser()` returns `null`, no active session exists and you need to call `login()`. - -| Method | Returns | Description | -| ------ | ------- | ----------- | -| `CometChat.getLoggedInUser()` | `Future` | Returns the currently logged-in user, or `null` if no session exists | + +The CometChat SDK maintains the session of the logged-in user within the SDK. Thus you do not need to call the login method for every session. You can use the CometChat.getLoggedInUser() method to check if there is any existing session in the SDK. This method should return the details of the logged-in user. If this method returns null, it implies there is no session present within the SDK and you need to log the user into ComeChat. -## Login with Auth Key + -This straightforward authentication method is ideal for proof-of-concept (POC) development or during the early stages of application development. For production environments, however, we strongly recommend using an [Auth Token](#login-with-auth-token) instead of an Auth Key to ensure enhanced security. +## Login using Auth Key - -Auth Keys are meant for development and testing only. For production, use [Auth Token login](#login-with-auth-token) instead. Never ship Auth Keys in client-side code. - +This straightforward authentication method is ideal for proof-of-concept (POC) development or during the early stages of application development. For production environments, however, we strongly recommend using an [AuthToken](#login-using-auth-token) instead of an Auth Key to ensure enhanced security. @@ -118,56 +89,20 @@ await CometChat.login(UID, authKey, -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `UID` | `String` | The UID of the user to log in | -| `authKey` | `String` | Your CometChat Auth Key | - -On success, the `onSuccess` callback receives a [`User`](/sdk/reference/entities#user) object containing the logged-in user's details. - - -**On Success** — A `User` object representing the logged-in user: - - - -**User Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | +| Parameter | Description | +| --------- | -------------------------------------------------- | +| UID | The `UID` of the user that you would like to login | +| authKey | CometChat App Auth Key | - +After the user logs in, their information is returned in the `User` object. - +## Login using Auth Token -**CometChatException:** +This advanced authentication procedure does not use the Auth Key directly in your client code thus ensuring safety. -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified UID does not exist."` | -| `details` | string | Additional technical details | `"Please verify the UID and try again."` | - - - -## Login with Auth Token - -Auth Token login keeps your Auth Key off the client entirely. Your server generates a token via the REST API and passes it to the client. - -1. [Create the user](https://api-explorer.cometchat.com/reference/creates-user) via the REST API when they sign up (first time only). -2. [Generate an Auth Token](https://api-explorer.cometchat.com/reference/create-authtoken) on your server and return it to the client. -3. Pass the token to `loginWithAuthToken()`. +1. [Create a User](https://api-explorer.cometchat.com/reference/creates-user) via the CometChat API when the user signs up in your app. +2. [Create an Auth Token](https://api-explorer.cometchat.com/reference/create-authtoken) via the CometChat API for the new user every time the user logs in to your app. +3. Pass the **Auth Token** to your client and use it in the `login()` method. @@ -190,51 +125,15 @@ if (user == null) { -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `authToken` | `String` | Auth Token generated on your server for the user | - -On success, the `onSuccess` callback receives a [`User`](/sdk/reference/entities#user) object containing the logged-in user's details. - - -**On Success** — A `User` object representing the logged-in user: - - - -**User Object:** +| Parameter | Description | +| ---------------------------------------------- | ----------- | +| authToken | Auth Token of the user you would like to login | -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - - - - - -**CometChatException:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified UID does not exist."` | -| `details` | string | Additional technical details | `"Please verify the UID and try again."` | - - +After the user logs in, their information is returned in the `User` object. ## Logout -Call `logout()` when your user logs out of your app. This clears the local session. +You can use the `logout()` method to log out the user from CometChat. We suggest you call this method once your user has been successfully logged out from your app. @@ -250,99 +149,3 @@ CometChat.logout( onSuccess: ( successMessage) { - - -**On Success** — A `String` message confirming the logout: - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"Logout successful"` | - - - - - -**CometChatException:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_NOT_LOGGED_IN"` | -| `message` | string | Human-readable error message | `"No active user session."` | -| `details` | string | Additional technical details | `"Please log in before attempting to log out."` | - - - ---- - -## Login Listener - -You can listen for login and logout events in real time using `LoginListener`. This is useful for updating UI state or triggering side effects when the auth state changes. - -| Callback | Description | -| --- | --- | -| `loginSuccess(User user)` | User logged in successfully. Provides the `User` object. | -| `loginFailure(CometChatException e)` | Login failed. Provides a `CometChatException`. | -| `logoutSuccess()` | User logged out successfully. | -| `logoutFailure(CometChatException e)` | Logout failed. Provides a `CometChatException`. | - -### Add a Listener - - - -```dart -class Class_Name with LoginListener { - - // CometChat.addLoginListener("UNIQUE_LISTENER_ID", this); // call this in initState - - @override - void loginSuccess(User user) { - debugPrint("LoginListener loginSuccess: $user"); - } - - @override - void loginFailure(CometChatException e) { - debugPrint("LoginListener loginFailure: ${e.message}"); - } - - @override - void logoutSuccess() { - debugPrint("LoginListener logoutSuccess"); - } - - @override - void logoutFailure(CometChatException e) { - debugPrint("LoginListener logoutFailure: ${e.message}"); - } -} -``` - - - -### Remove a Listener - -```dart -CometChat.removeLoginListener("UNIQUE_LISTENER_ID"); -``` - - -Always remove login listeners when they're no longer needed (e.g., on widget disposal or when navigating away). Failing to remove listeners can cause memory leaks and duplicate event handling. - - ---- - -## Next Steps - - - - Send your first text, media, or custom message - - - Create, update, and manage users in your app - - - Monitor the SDK connection state in real time - - - Complete reference for all SDK event listeners - - diff --git a/sdk/flutter/block-users.mdx b/sdk/flutter/block-users.mdx index 6370279cd..8613e6df1 100644 --- a/sdk/flutter/block-users.mdx +++ b/sdk/flutter/block-users.mdx @@ -1,54 +1,14 @@ --- title: "Block Users" -sidebarTitle: "Block Users" -description: "Learn how to block and unblock users in your Flutter app using the CometChat SDK to manage user interactions and privacy." --- - -```dart -// Block users -List uids = ["UID1", "UID2"]; -CometChat.blockUser(uids, onSuccess: (Map map) { - debugPrint("Blocked: $map"); -}, onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); -}); - -// Unblock users -CometChat.unblockUser(uids, onSuccess: (Map map) { - debugPrint("Unblocked: $map"); -}, onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); -}); - -// Get blocked users list -BlockedUsersRequest request = (BlockedUsersRequestBuilder()..limit = 30).build(); -request.fetchNext(onSuccess: (List users) { }, onError: (e) { }); -``` - -**Directions:** `directionBlockedByMe` | `directionHasBlockedMe` | `directionBoth` (default) - - -Block users to prevent them from sending messages to the logged-in user. This feature helps users manage their privacy and control who can communicate with them. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - ## Block Users *In other words, as a logged-in user, how do I block a user from sending me messages?* -You can block users using the `blockUser()` method. Once any user is blocked, all the communication to and from the respective user will be completely blocked. You can block multiple users in a single operation. The `blockUser()` method takes a `List` as a parameter which holds the list of `UIDs` to be blocked. - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `uids` | `List?` | List of UIDs of users to block | -| `onSuccess` | `Function(Map map)?` | Callback triggered on successful block operation | -| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | +You can block users using the `blockUsers()` method. Once any user is blocked, all the communication to and from the respective user will be completely blocked. You can block multiple users in a single operation. The `blockUsers()` method takes a `List` as a parameter which holds the list of `UIDs` to be blocked. @@ -68,46 +28,13 @@ debugPrint("Blocked User Unsuccessful ${e.message} "); - -**On Success** — A `Map` containing UIDs as keys and `"success"` or `"fail"` as values indicating the result of each block operation: - - - -**Map Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `UID1` | string | Block result for UID1 | `"success"` | -| `UID2` | string | Block result for UID2 | `"success"` | -| `UID3` | string | Block result for UID3 | `"success"` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified UID does not exist."` | -| `details` | string | Additional technical details | `"Please verify the UID and try again."` | - - - In the `onSuccess()` callback, you receive a Map which contains `UIDs` as the keys and `success` or `fail` as the value based on if the block operation for the `UID` was successful or not. ## Unblock Users *In other words, as a logged-in user, how do I unblock a user I previously blocked?* -You can unblock the already blocked users using the `unblockUser()` method. You can unblock multiple users in a single operation. The `unblockUser()` method takes a `List` as a parameter which holds the list of `UIDs` to be unblocked. - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `uids` | `List?` | List of UIDs of users to unblock | -| `onSuccess` | `Function(Map map)?` | Callback triggered on successful unblock operation | -| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | +You can unblock the already blocked users using the `unblockUsers()` method. You can unblock multiple users in a single operation. The `unblockUsers()` method takes a `List` as a parameter which holds the list of `UIDs` to be unblocked. @@ -128,31 +55,6 @@ CometChat.unblockUser(uids, onSuccess: (Map map) { - -**On Success** — A `Map` containing UIDs as keys and `"success"` or `"fail"` as values indicating the result of each unblock operation: - - - -**Map Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `UID1` | string | Unblock result for UID1 | `"success"` | -| `UID2` | string | Unblock result for UID2 | `"success"` | -| `UID3` | string | Unblock result for UID3 | `"success"` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified UID does not exist."` | -| `details` | string | Additional technical details | `"Please verify the UID and try again."` | - - - In the `onSuccess()` callback, you receive a Map which contains `UIDs` as the keys and `success` or `fail` as the value based on if the unblock operation for the `UID` was successful or not. ## Get list of blocked users @@ -163,15 +65,6 @@ In order to fetch the list of blocked users, you can use the `BlockedUsersReques The `BlockedUsersRequestBuilder` class allows you to set the below parameters: -### BlockedUsersRequestBuilder - -| Parameter | Type | Description | -|-----------|------|-------------| -| `limit` | `int?` | Number of blocked users to fetch per request. Max: 100, Default: 30 | -| `searchKeyword` | `String?` | Keyword to filter blocked users by name | -| `direction` | `String?` | Direction of block — `directionBlockedByMe`, `directionHasBlockedMe`, or `directionBoth` (default) | -| `setPage` | `int?` | Fetch blocked users from a particular page | - ### Set Limit This method sets the limit i.e. the number of blocked users that should be fetched in a single iteration. @@ -216,7 +109,7 @@ BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() ```dart BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() ..limit = 50 - ..direction = CometChatBlockedUsersDirection.directionBlockedByMe + .direction = CometChatBlockedUsersDirection.directionBlockedByMe ).build(); ``` @@ -245,63 +138,3 @@ blockedUsersRequest.fetchNext(onSuccess: (List userList){ - - -**On Success** — A `List` containing the blocked users: - - - -**User Object (per item in list):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-3"` | -| `name` | string | Display name of the user | `"Kevin Hart"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `true` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified UID does not exist."` | -| `details` | string | Additional technical details | `"Please verify the UID and try again."` | - - - -Relevant fields to access on returned [`User`](/sdk/reference/entities#user) objects: - -| Field | Type | Description | -|-------|------|-------------| -| `blockedByMe` | `bool` | Whether the logged-in user has blocked this user | -| `hasBlockedMe` | `bool` | Whether this user has blocked the logged-in user | - ---- - -## Next Steps - - - - Fetch and filter users from your CometChat app - - - Create, update, and delete users programmatically - - - Track online/offline status of users in real-time - - - Complete guide to user features in CometChat - - diff --git a/sdk/flutter/changelog.mdx b/sdk/flutter/changelog.mdx index d482ab3bb..eb92a1622 100644 --- a/sdk/flutter/changelog.mdx +++ b/sdk/flutter/changelog.mdx @@ -1,6 +1,4 @@ --- title: "Changelog" -sidebarTitle: "Changelog" url: "https://github.com/cometchat/chat-sdk-flutter/releases" -description: "Navigate to Changelog documentation." --- \ No newline at end of file diff --git a/sdk/flutter/connection-behaviour.mdx b/sdk/flutter/connection-behaviour.mdx index fb2e027ee..ba5b683a9 100644 --- a/sdk/flutter/connection-behaviour.mdx +++ b/sdk/flutter/connection-behaviour.mdx @@ -1,36 +1,8 @@ --- title: "Connection Behaviour" -sidebarTitle: "Connection Behaviour" -description: "Understand how CometChat SDK manages WebSocket connections in auto and manual modes, including background behavior and reconnection handling." --- - -```dart -// Auto Mode (default) - SDK manages connection automatically -AppSettings appSettings = (AppSettingsBuilder() - ..subscriptionType = CometChatSubscriptionType.allUsers - ..region = "REGION" - ..autoEstablishSocketConnection = true // Default behavior -).build(); - -// Manual Mode - You control the connection -AppSettings appSettings = (AppSettingsBuilder() - ..subscriptionType = CometChatSubscriptionType.allUsers - ..region = "REGION" - ..autoEstablishSocketConnection = false // Manual control -).build(); - -// Manual mode methods -CometChat.connect(onSuccess: (msg) {}, onError: (e) {}); // Establish connection -CometChat.disconnect(onSuccess: (msg) {}, onError: (e) {}); // Break connection -CometChat.ping(onSuccess: () {}, onError: (e) {}); // Keep alive in background -``` - -**Connection Modes:** -- **Auto Mode:** SDK manages WebSocket automatically (foreground=connected, background=disconnected) -- **Manual Mode:** You control connect/disconnect; call `ping()` every 30s to keep background connection alive - ## Default SDK behaviour on login @@ -46,8 +18,6 @@ When the app is reopened, and the init() method is called, the web-socket connec This is the default behaviour of the CometChat SDKs. However, if you wish to take control of the web-socket connection i.e if you wish to connect and disconnect to the web-socket server manually, you can refer to the Managing Web-socket Connection section. -You'd want manual control when you need to conserve resources by connecting only when the user is actively chatting, or when you need precise control over when real-time events start flowing. - ## Auto Mode CometChat SDK default connection behaviour is auto mode. Auto mode, the SDK automatically establishes and maintains the WebSocket connection. You do not need to explicitly call any methods to do this. @@ -120,25 +90,6 @@ CometChat.init(appId, appSettings, onSuccess: (String successMessage) { - -**On Success** — A `String` message confirming SDK initialization: - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"Initialization completed successfully"` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"SDK initialization failed."` | -| `details` | string | Additional technical details | `"Please verify your App ID and region, then try again."` | - - - You can manage the connection to the web-socket server using the `connect()` and `disconnect()` methods provided by the SDK. ## Connect to the web-socket server @@ -162,25 +113,6 @@ CometChat.connect( - -**On Success** — A `String` message confirming the WebSocket connection was established: - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"Web socket connection successful"` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_NOT_LOGGED_IN"` | -| `message` | string | Human-readable error message | `"No active user session."` | -| `details` | string | Additional technical details | `"Please log in before establishing a WebSocket connection."` | - - - ## Disconnect from the web-socket server You can use the `disconnect()` method provided by the `CometChat` class to break the established connection. Once the connection is broken, you will stop receiving all the real-time events for the logged in user. @@ -202,25 +134,6 @@ CometChat.disconnect( - -**On Success** — A `String` message confirming the WebSocket connection was disconnected: - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"Web socket disconnection successful"` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_NOT_LOGGED_IN"` | -| `message` | string | Human-readable error message | `"No active user session."` | -| `details` | string | Additional technical details | `"Please log in before disconnecting the WebSocket connection."` | - - - ## Maintain long-standing background connection @@ -248,44 +161,6 @@ CometChat.ping( - -**On Success** — A confirmation that the ping was sent successfully (no return value): - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation | `"ping successful"` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_NOT_LOGGED_IN"` | -| `message` | string | Human-readable error message | `"No active user session."` | -| `details` | string | Additional technical details | `"Please log in before sending a ping to the server."` | - - - ## Reconnection If manual mode is enabled and the app is in the foreground, the SDK will automatically reconnect the WebSocket if the internet connection is lost. However, if the app is in the background and the WebSocket is disconnected or you called `CometChat.disconnect()`, then you will need to call the `CometChat.connect()` method to create a new WebSocket connection. - ---- - -## Next Steps - - - - Monitor real-time WebSocket connection status with listeners - - - Configure CometChat SDK initialization and settings - - - Learn about all available real-time event listeners - - - Implement user login and logout functionality - - diff --git a/sdk/flutter/connection-status.mdx b/sdk/flutter/connection-status.mdx index 890d70074..a0877d610 100644 --- a/sdk/flutter/connection-status.mdx +++ b/sdk/flutter/connection-status.mdx @@ -1,89 +1,24 @@ --- title: "Connection Status" -sidebarTitle: "Connection Status" -description: "Monitor real-time WebSocket connection status with CometChat SDK using ConnectionListener callbacks and getConnectionStatus method." --- - -```dart -// Add connection listener -CometChat.addConnectionListener("connection_listener", ConnectionListenerImpl()); - -// Connection listener implementation -class ConnectionListenerImpl with ConnectionListener { - @override - void onConnected() => debugPrint("Connected"); - - @override - void onConnecting() => debugPrint("Connecting..."); - - @override - void onDisconnected() => debugPrint("Disconnected"); - - @override - void onFeatureThrottled() => debugPrint("Feature throttled"); - - @override - void onConnectionError(CometChatException error) => debugPrint("Error: ${error.message}"); -} - -// Check current connection status -String status = CometChat.getConnectionStatus(); -// Returns: CometChatWSState.connected, connecting, disconnected, or featureThrottled - -// Remove listener when done -CometChat.removeConnectionListener("connection_listener"); -``` - - -The CometChat SDK maintains a WebSocket connection to CometChat servers for real-time events. You can check the current connection state and listen for changes — useful for showing connectivity indicators in your UI or queuing operations while offline. - -When the connection drops, the SDK automatically attempts to reconnect, cycling through `disconnected` → `connecting` → `connected`. - -## Connection States - -| Value | Callback | Description | -|-------|----------|-------------| -| `CometChatWSState.connected` | `onConnected()` | SDK has an active connection to CometChat servers | -| `CometChatWSState.connecting` | `onConnecting()` | SDK is attempting to establish or re-establish a connection | -| `CometChatWSState.disconnected` | `onDisconnected()` | SDK is disconnected due to network issues or other errors | -| `CometChatWSState.featureThrottled` | `onFeatureThrottled()` | A feature has been throttled to prevent performance loss | -| — | `onConnectionError(CometChatException)` | An error occurred while maintaining the connection | - -## Get Current Status - -Use `getConnectionStatus()` to check the current connection state at any time: - - - -```dart -String connectionStatus = CometChat.getConnectionStatus(); -``` - - - -The method returns one of the following values: -1. `CometChatWSState.connected` (connected) -2. `CometChatWSState.connecting` (connecting) -3. `CometChatWSState.disconnected` (disconnected) -4. `CometChatWSState.featureThrottled` (featureThrottled) +CometChat SDK provides you with a mechanism to get real-time status of the connection to CometChat web-socket servers. To achieve this you need to use the `ConnectionListener` class provided by the CometChat SDK -## Listen for Connection Changes +Connection Status provides you with the below 3 methods to get the status of the connection to CometChat web-socket servers: -Register a `ConnectionListener` to receive real-time connection state updates. We recommend adding this on app startup after `CometChat.init()` completes. +| Delegate Method | Information | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| onConnecting | This method is triggered when CometChat SDK is trying to establish a connection to the web-socket server. | +| onConnected | This method is called when CometChat SDK has successfully established a connection and now is connected. | +| onDisconnected | This method is called when the CometChat SDK gets disconnected due to any issue while maintaining the connection like network fluctuations, etc. | +| onFeatureThrottled | CometChat automatically toggles off certain features to prevent performance loss for end-users under various circumstances | +| onConnectionError | This method is called when the CometChat SDK gets error due to any issue while maintaining the connection like network fluctuations, etc. | -### ConnectionListener Events - -| Event | Parameter | Description | -|-------|-----------|-------------| -| `onConnected()` | — | Triggered when the SDK successfully establishes a connection to the WebSocket server | -| `onConnecting()` | — | Triggered when the SDK is attempting to establish a connection to the WebSocket server | -| `onDisconnected()` | — | Triggered when the SDK gets disconnected due to network fluctuations or other issues | -| `onFeatureThrottled()` | — | Triggered when CometChat automatically toggles off certain features to prevent performance loss | -| `onConnectionError(CometChatException error)` | `CometChatException` | Triggered when an error occurs while maintaining the WebSocket connection | +Once the connection is broken, the disconnected callback is triggered, the SDK automatically tries to establish the connection again, thus going into the connecting state and triggering the `connecting` method. Once the attempt to connect is successful, the `connected` method is triggered thus letting the developer know that the connection is established and is active. +In order to use the ConnectionListeners, you need to add the ConnectionListeners using the `addConnectionListener` method provided by the SDK. You can add multiple listeners as shown below. Just make sure you add listeners with unique IDs. @@ -125,37 +60,27 @@ void onConnectionError(CometChatException error) { - -Always remove connection listeners when they're no longer needed (e.g., on widget dispose or navigation). Failing to remove listeners can cause memory leaks and duplicate event handling. - - -### Remove Connection Listener +You can also get the current connection status by using `getConnectionStatus` property provided by CometChat SDK + + ```dart -CometChat.removeConnectionListener("listenerId"); +String connectionStatus = CometChat.getConnectionStatus(); ``` + + + + +The above method will return either of the below 3 values: + +1. `CometChatWSState.connected` (connected); +2. `CometChatWSState.connecting`(connecting) +3. `CometChatWSState.disconnected`(disconnected) +4. `CometChatWSState.featureThrottled`(featureThrottled) + Know more about CometChat SDK connection behaviour [click here](/sdk/flutter/connection-behaviour) - ---- - -## Next Steps - - - - Understand how CometChat SDK manages WebSocket connections - - - Monitor user login and logout events in real-time - - - Complete reference for all SDK listeners - - - Install and initialize the CometChat SDK - - diff --git a/sdk/flutter/create-group.mdx b/sdk/flutter/create-group.mdx index 0c6157da2..feb4149fc 100644 --- a/sdk/flutter/create-group.mdx +++ b/sdk/flutter/create-group.mdx @@ -1,59 +1,22 @@ --- title: "Create A Group" -sidebarTitle: "Create Group" -description: "Create public, private, or password-protected groups and optionally add members during creation using the CometChat Flutter SDK." --- - -```dart -// Create a group -Group group = Group(guid: "GUID", name: "Group Name", type: CometChatGroupType.public); -await CometChat.createGroup( - group: group, - onSuccess: (Group group) => debugPrint("Created: ${group.name}"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Create group with members -CometChat.createGroupWithMembers( - group: group, - groupMembers: [GroupMember(uid: "UID", scope: GroupMemberScope.participant)], - bannedUserIds: [], - onSuccess: (Group group) => debugPrint("Created with members: ${group.name}"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); -``` - -**Group types:** `CometChatGroupType.public` | `CometChatGroupType.password` | `CometChatGroupType.private` -**Member scopes:** `GroupMemberScope.admin` | `GroupMemberScope.moderator` | `GroupMemberScope.participant` - - -Create groups for multi-user conversations. You can create a group on its own with `createGroup()`, or create one and add members in a single call with `createGroupWithMembers()`. See the [Group Class](#group-class) reference at the bottom for all available fields. ## Create a Group *In other words, as a logged-in user, how do I create a public, private or password-protected group?* - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +You can create a group using `createGroup()` method. This method takes a `Group` object as input. -Use `createGroup()` to create a new group. Pass a `Group` object with the group details. +The `groupType` needs to be either of the below 3 values: -| Group Type | Constant | Description | -| --- | --- | --- | -| Public | `CometChatGroupType.public` | Any user can join | -| Password | `CometChatGroupType.password` | Users must provide the correct password | -| Private | `CometChatGroupType.private` | Users must be added by an admin/moderator | +1.`CometChatGroupType.`*public* (public) -### Parameters +2.`CometChatGroupType.`*password* (password) -| Parameter | Type | Description | -|-----------|------|-------------| -| `group` | `Group` | An instance of the `Group` class containing group details (guid, name, type, password) | -| `onSuccess` | `Function(Group group)?` | Callback triggered on successful group creation | -| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | +3.`CometChatGroupType.`*private* (private) @@ -77,45 +40,13 @@ String GUID = "GUID"; - -**On Success** — A `Group` object containing all details of the newly created group: - - - -**Group Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | -| `name` | string | Display name of the group | `"Hello Group!"` | -| `icon` | string | URL of the group icon | `null` | -| `description` | string | Description of the group | `null` | -| `membersCount` | number | Number of members in the group | `1` | -| `metadata` | object | Custom metadata attached to the group | `{}` | -| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | -| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | -| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | -| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | -| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | -| `tags` | array | List of tags associated with the group | `[]` | -| `type` | string | Type of the group (public, private, password) | `"public"` | -| `scope` | string | Scope of the logged-in user in the group | `"admin"` | -| `password` | string | Password for password-protected groups | `null` | -| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | - - - - +The `createGroup()` method takes the following parameters: -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified group does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | +| Parameter | Description | +| --------- | ---------------------------- | +| `group` | An instance of `Group` class | - - -On success, returns a [`Group`](/sdk/reference/entities#group) object with the created group's details. +After the successful creation of the group, you will receive an instance of `Group` class which contains all the information about the particular group. @@ -123,45 +54,39 @@ GUID can be alphanumeric with underscore and hyphen. Spaces, punctuation and oth -## Add Members While Creating a Group - -Use `createGroupWithMembers()` to create a group and add members in one operation. +## Create a Group with Members -### Parameters +*In other words, how do I create a group and add members in a single step?* -| Parameter | Type | Description | -|-----------|------|-------------| -| `group` | `Group` | The `Group` object with group details (guid, name, type, password) | -| `groupMembers` | `List` | List of `GroupMember` objects to add during creation | -| `bannedUserIds` | `List` | List of UIDs to ban upon creation (defaults to empty) | -| `onSuccess` | `Function(Group group)?` | Callback triggered on successful creation | -| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | - -Create a `GroupMember` with: `GroupMember(uid: "UID", scope: GroupMemberScope.participant)` +You can create a group and add members simultaneously using the `createGroupWithMembers()` method. This is more efficient than creating a group first and then adding members separately. ```dart -String GUID = "cometchat-guid-11"; +String GUID = "GUID"; String groupName = "Hello Group!"; String groupType = CometChatGroupType.public; Group group = Group(guid: GUID, name: groupName, type: groupType); -List members = [ - GroupMember(uid: "cometchat-uid-1", scope: GroupMemberScope.participant), -]; -List bannedUserIds = ["cometchat-uid-2"]; -CometChat.createGroupWithMembers( +// Create a list of group members to add +List members = []; +members.add(GroupMember(uid: "cometchat-uid-1", scope: CometChatMemberScope.participant)); +members.add(GroupMember(uid: "cometchat-uid-2", scope: CometChatMemberScope.moderator)); +members.add(GroupMember(uid: "cometchat-uid-3", scope: CometChatMemberScope.admin)); + +await CometChat.createGroupWithMembers( group: group, - groupMembers: members, - bannedUserIds: bannedUserIds, - onSuccess: (Group group) { - debugPrint("Group created with members: ${group.name}"); + members: members, + onSuccess: (Group group, Map failedMembers) { + debugPrint("Group Created Successfully : $group"); + if (failedMembers.isNotEmpty) { + debugPrint("Failed to add some members: $failedMembers"); + } }, onError: (CometChatException e) { - debugPrint("Error creating group with members: ${e.message}"); - }, + debugPrint("Group Creation failed with exception: ${e.message}"); + } ); ``` @@ -169,48 +94,26 @@ CometChat.createGroupWithMembers( - -**On Success** — A `Group` object containing all details of the newly created group: +The `createGroupWithMembers()` method takes the following parameters: - +| Parameter | Description | +| --------- | ---------------------------------------------------------- | +| `group` | An instance of `Group` class | +| `members` | A list of `GroupMember` objects to be added to the group | -**Group Object:** +The `onSuccess` callback returns: +- `group`: The created `Group` object +- `failedMembers`: A map containing UIDs of members that failed to be added along with the error reason -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `guid` | string | Unique identifier for the group | `"cometchat-guid-11"` | -| `name` | string | Display name of the group | `"Hello Group!"` | -| `icon` | string | URL of the group icon | `null` | -| `description` | string | Description of the group | `null` | -| `membersCount` | number | Number of members in the group | `2` | -| `metadata` | object | Custom metadata attached to the group | `{}` | -| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | -| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | -| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | -| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | -| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | -| `tags` | array | List of tags associated with the group | `[]` | -| `type` | string | Type of the group (public, private, password) | `"public"` | -| `scope` | string | Scope of the logged-in user in the group | `"admin"` | -| `password` | string | Password for password-protected groups | `null` | -| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | +The `GroupMember` class takes the following parameters: - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified group does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | - - +| Parameter | Description | +| --------- | ------------------------------------------------------------------------------------------------ | +| `uid` | The UID of the user to be added as a member | +| `scope` | The scope of the member: `CometChatMemberScope.participant`, `moderator`, or `admin` | ## Group Class -The [`Group`](/sdk/reference/entities#group) object has the following fields. Fields marked "Yes" in the Editable column can be modified after creation using `updateGroup()`. - | Field | Editable | Information | | ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------- | | guid | Needs to be specified at group creation. Cannot be edited later | A unique identifier for a group | @@ -228,22 +131,4 @@ The [`Group`](/sdk/reference/entities#group) object has the following fields. Fi | scope | Yes | Scope of the logged in user. Can be: 1. Admin 2. Moderator 3. Participant | | membersCount | No | The number of members in the groups | | tags | Yes | A list of tags to identify specific groups. | - ---- - -## Next Steps - - - - Join public, private, or password-protected groups - - - Add users to an existing group - - - Fetch and filter group lists - - - Overview of all group management features - - +| isBannedFromGroup | No | A boolean indicating whether the logged-in user is banned from the group. | diff --git a/sdk/flutter/delete-conversation.mdx b/sdk/flutter/delete-conversation.mdx index 7eed0b031..57473b8ca 100644 --- a/sdk/flutter/delete-conversation.mdx +++ b/sdk/flutter/delete-conversation.mdx @@ -1,44 +1,12 @@ --- -title: "Delete Conversation" -sidebarTitle: "Delete Conversation" -description: "Learn how to delete user and group conversations from the logged-in user's conversation list using the CometChat Flutter SDK." +title: "Delete A Conversation" --- -{/* TL;DR for Agents and Quick Reference */} - -```dart -// Delete a user conversation -await CometChat.deleteConversation( - "UID", - CometChatConversationType.user, - onSuccess: (String message) => debugPrint("Deleted: $message"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Delete a group conversation -await CometChat.deleteConversation( - "GUID", - CometChatConversationType.group, - onSuccess: (String message) => debugPrint("Deleted: $message"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); -``` -**Note:** Deletes only for the logged-in user. Use [REST API](https://api-explorer.cometchat.com/reference/deletes-conversation) to delete for all participants. - +In case you want to delete a conversation, you can use the `deleteConversation()` method. - -This operation is irreversible. Deleted conversations cannot be recovered. All messages in the conversation will be removed from the user's view. - - -Use `deleteConversation()` to delete a conversation for the logged-in user. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - - -This method takes two parameters: the unique id (`UID`/`GUID`) of the conversation to be deleted and the type (`user`/`group`) of conversation to be deleted. +This method takes two parameters. The unique id (UID/GUID) of the conversation to be deleted & the type (user/group) of conversation to be deleted. @@ -75,51 +43,11 @@ await CometChat.deleteConversation(conversationWith, conversationType, -This method deletes the conversation only for the logged-in user. To delete a conversation for all the users of the conversation, please refer to the [REST API documentation](https://api-explorer.cometchat.com/reference/deletes-conversation). - -| Parameter | Type | Description | Required | -| --- | --- | --- | --- | -| `conversationWith` | `String` | `UID` of the user or `GUID` of the group whose conversation you want to delete. | Yes | -| `conversationType` | `String` | The type of conversation to delete. Use `CometChatConversationType.user` or `CometChatConversationType.group`. | Yes | - - -**On Success** — A `String` message confirming the conversation was deleted: - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"cometchat-uid-1_user_cometchat-uid-2 deleted successfully"` | - - - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to delete the conversation."` | -| `details` | string | Additional technical details | `"The specified conversation could not be found or deleted."` | - - - ---- +This method deletes the conversation only for the logged-in user. To delete a conversation for all the users of the conversation, please refer to our REST API documentation [here](https://api-explorer.cometchat.com/reference/deletes-conversation). -## Next Steps +The `deleteConversation()` method takes the following parameters: - - - Fetch and filter conversation lists - - - Delete individual messages from conversations - - - Listen for incoming messages in real-time - - - Show when users are typing in conversations - - +| Parameter | Description | Required | +| ---------------- | --------------------------------------------------------------------------------- | -------- | +| conversationWith | `UID` of the user or `GUID` of the group whose conversation you want to delete. | YES | +| conversationType | The type of conversation you want to delete . It can be either `user` or `group`. | YES | diff --git a/sdk/flutter/delete-group.mdx b/sdk/flutter/delete-group.mdx index 4f48f7e14..2bc36f2e0 100644 --- a/sdk/flutter/delete-group.mdx +++ b/sdk/flutter/delete-group.mdx @@ -1,49 +1,12 @@ --- title: "Delete A Group" -sidebarTitle: "Delete Group" -description: "Delete a group permanently using the CometChat Flutter SDK. Only the group owner can perform this operation." --- - -```dart -// Delete a group (owner only) -String guid = "GROUP_ID"; - -CometChat.deleteGroup(guid, - onSuccess: (String message) { - debugPrint("Deleted: $message"); - }, - onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); - }, -); -``` - -**Requirement:** Logged-in user must be the owner of the group. - - -Permanently delete a group and all its messages. Only the group owner can perform this operation. The group is represented by a [`Group`](/sdk/reference/entities#group) object. - -This operation is irreversible. Deleted groups and their messages cannot be recovered. - +## Delete Group -## Delete a Group - -Use `deleteGroup()` with the group's GUID. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `guid` | `String` | The GUID of the group you would like to delete | -| `onSuccess` | `Function(String message)?` | Callback triggered on successful deletion | -| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | +To delete a group you need to use the `deleteGroup()` method. The user must be an **Admin** of the group they are trying to delete. @@ -61,44 +24,8 @@ debugPrint("Delete Group failed with exception: ${e.message}"); - -**On Success** — A `String` message confirming the operation: - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"cometchat-guid-1 deleted successfully"` | - - - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified group does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid group GUID."` | - - - ---- - -## Next Steps +The `deleteGroup()` method takes the following parameters: - - - Update group name, icon, description, and metadata - - - Leave a group you are a member of - - - Create public, private, or password-protected groups - - - Overview of all group management features - - +| Parameter | Description | +| --------- | ---------------------------------------------- | +| `GUID` | The GUID of the group you would like to delete | diff --git a/sdk/flutter/delete-message.mdx b/sdk/flutter/delete-message.mdx index 0240e6336..ebf63f117 100644 --- a/sdk/flutter/delete-message.mdx +++ b/sdk/flutter/delete-message.mdx @@ -1,70 +1,19 @@ --- title: "Delete A Message" -sidebarTitle: "Delete Message" -description: "Delete messages and handle real-time deletion events using the CometChat Flutter SDK." --- -{/* TL;DR for Agents and Quick Reference */} - -| Field | Value | -| --- | --- | -| Key Classes | `BaseMessage`, `CometChatException` | -| Key Methods | `CometChat.deleteMessage()` | -| Listener Events | `onMessageDeleted` | -| Prerequisites | SDK initialized, user logged in | - -```dart -// Delete a message by ID -int messageId = 1234; -await CometChat.deleteMessage(messageId, - onSuccess: (BaseMessage message) { - debugPrint("Message deleted at: ${message.deletedAt}"); - }, - onError: (CometChatException e) { - debugPrint("Delete failed: ${e.message}"); - }, -); - -// Listen for deleted messages -CometChat.addMessageListener("listener_id", MessageListener( - onMessageDeleted: (BaseMessage message) { - debugPrint("Message ${message.id} was deleted"); - }, -)); - -// Remove listener when done -CometChat.removeMessageListener("listener_id"); -``` - -**Who can delete:** Message sender, Group admin, Group moderator -**Deleted fields:** `deletedAt` (timestamp), `deletedBy` (user who deleted) - - - -This operation is irreversible. Deleted messages cannot be recovered. - While [deleting a message](/sdk/flutter/delete-message#delete-a-message) is straightforward, receiving events for deleted messages with CometChat has two parts: 1. Adding a listener to receive [real-time message deletes](/sdk/flutter/delete-message#real-time-message-delete-events) when your app is running. -2. Calling a method to retrieve [missed message delete events](/sdk/flutter/delete-message#missed-message-delete-events) when your app was not running. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +2. Calling a method to retrieve [missed message delete events](/sdk/flutter/delete-message#missed-message-delete-events)-me when your app was not running. ## Delete a Message *In other words, as a sender, how do I delete a message?* -Use `deleteMessage()` with the message ID of the message to be deleted. - -| Parameter | Type | Description | -| --- | --- | --- | -| `messageId` | `int` | The ID of the message to delete. | -| `onSuccess` | `Function(BaseMessage)` | Callback triggered on success with the deleted message object. | -| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | +In case you have to delete a message, you can use the `deleteMessage()` method. This method takes the message ID of the message to be deleted. @@ -84,103 +33,13 @@ await CometChat.deleteMessage(messageId, - -**On Success** — A `BaseMessage` object with `deletedAt` and `deletedBy` fields set: - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `1234` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#delete-message-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `"cometchat-uid-1"` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554750` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `1745554800` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#delete-message-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554800` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | +Once the message is deleted, In the `onSuccess()` callback, you get an object of the `BaseMessage` class, with the `deletedAt` field set with the timestamp of the time the message was deleted. Also, the `deletedBy` field is set. These two fields can be used to identify if the message is deleted while iterating through a list of messages. - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"The message could not be deleted."` | -| `details` | string | Additional technical details | `"Ensure the message ID is valid and you have permission to delete this message."` | - - - -The deleted message object is returned with `deletedAt` (timestamp) and `deletedBy` (UID of deleter) fields set. - -Relevant fields to access on the returned message: +By default, CometChat allows certain roles to delete a message. -| Field | Property | Return Type | Description | -|-------|----------|-------------|-------------| -| deletedAt | `deletedAt` | `int` | Timestamp when the message was deleted | -| deletedBy | `deletedBy` | `String` | UID of the user who deleted the message | +## Real-time Message Delete Events -By default, CometChat allows certain roles to delete a message. +*In other words, as a recipient, how do I know when someone deletes a message when my app is running?* | User Role | Conversation Type | Deletion Capabilities | | --------------- | ----------------------- | ------------------------- | @@ -189,11 +48,7 @@ By default, CometChat allows certain roles to delete a message. | Group Admin | Group Conversation | All messages in the group | | Group Moderator | Group Conversation | All messages in the group | -## Real-time Message Delete Events - -*In other words, as a recipient, how do I know when someone deletes a message when my app is running?* - -Use `onMessageDeleted` in `MessageListener` to receive real-time delete events. +In order to receive real-time events for a message being deleted, you need to override the `onMessageDeleted()` method of the `MessageListener` class. @@ -214,27 +69,6 @@ void onMessageDeleted(BaseMessage message) { - -Always remove message listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. - -```dart -@override -void dispose() { - CometChat.removeMessageListener("listenerId"); - super.dispose(); -} -``` - - -The `onMessageDeleted` callback receives a [`BaseMessage`](/sdk/reference/messages#basemessage) object with the `deletedAt` and `deletedBy` fields set. - -Relevant fields to access on the returned message: - -| Field | Property | Return Type | Description | -|-------|----------|-------------|-------------| -| deletedAt | `deletedAt` | `int` | Timestamp when the message was deleted | -| deletedBy | `deletedBy` | `String` | UID of the user who deleted the message | - ## Missed Message Delete Events *In other words, as a recipient, how do I know if someone deleted a message when my app was not running?* @@ -249,24 +83,7 @@ For the message deleted event, in the `Action` object received, the following fi 4. `actionFor` - User/group object having the details of the receiver to which the message was sent. -You must be the message sender or a group admin/moderator to delete a message. - ---- - -## Next Steps +In order to delete a message, you need to be either the sender of the message or the admin/moderator of the group in which the message was sent. - - - Modify sent messages before deletion - - - Send text, media, and custom messages - - - Handle incoming messages in real-time - - - Report inappropriate messages - - + diff --git a/sdk/flutter/delivery-read-receipts.mdx b/sdk/flutter/delivery-read-receipts.mdx index ca2605290..19be057f3 100644 --- a/sdk/flutter/delivery-read-receipts.mdx +++ b/sdk/flutter/delivery-read-receipts.mdx @@ -1,81 +1,29 @@ --- title: "Delivery & Read Receipts" -sidebarTitle: "Delivery & Read Receipts" -description: "Mark messages as delivered, read, or unread and receive real-time receipt events using the CometChat Flutter SDK." --- - -| Field | Value | -| --- | --- | -| Key Classes | `MessageReceipt`, `BaseMessage`, `Conversation` | -| Key Methods | `CometChat.markAsDelivered()`, `CometChat.markAsRead()`, `CometChat.markMessageAsUnread()`, `CometChat.getMessageReceipts()` | -| Listener Events | `onMessagesDelivered`, `onMessagesRead`, `onMessagesDeliveredToAll`, `onMessagesReadByAll` | -| Prerequisites | SDK initialized, user logged in | - -```dart -// Mark message as delivered -CometChat.markAsDelivered(message, onSuccess: (String unused) { - debugPrint("Marked as delivered"); -}, onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); -}); - -// Mark message as read -CometChat.markAsRead(message, onSuccess: (String unused) { - debugPrint("Marked as read"); -}, onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); -}); - -// Mark message as unread -CometChat.markMessageAsUnread(message, onSuccess: (Conversation conversation) { - debugPrint("Marked as unread"); -}, onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); -}); - -// Listen for receipts -CometChat.addMessageListener("receipts_listener", MessageListener( - onMessagesDelivered: (MessageReceipt receipt) { }, - onMessagesRead: (MessageReceipt receipt) { }, - onMessagesDeliveredToAll: (MessageReceipt receipt) { }, - onMessagesReadByAll: (MessageReceipt receipt) { }, -)); - -// Remove listener when done -CometChat.removeMessageListener("receipts_listener"); -``` - - - -Delivery and read receipts allow you to track when messages have been delivered to and read by recipients, providing real-time feedback on message status. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - ## Mark Messages as Delivered *In other words, as a recipient, how do I inform the sender that I've received a message?* -You can mark the messages for a particular conversation as delivered using the `markAsDelivered()` method. This method takes a `BaseMessage` object as input. +You can mark the messages for a particular conversation as read using the `markAsDelivered()` method. This method takes the below parameters as input: +| Parameter | Information | +| ------------ | ---------------------------------------------------------------------------------------------------------- | +| id | The ID of the message above which all the messages for a particular conversation are to be marked as read. | +| receiverUid | In case of one to one conversation message's sender UID will be the receipt's receiver Id. | +| receiverType | Type of the receiver. Could be either of the two values( user or group). | -| Parameter | Type | Description | -| --- | --- | --- | -| `message` | [`BaseMessage`](/sdk/reference/messages#basemessage) | The message object to mark as delivered. All messages before this message in the conversation will be marked as delivered. | -| `onSuccess` | `Function(String)` | Callback triggered on success with a confirmation string. | -| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | - -Messages for both user & group conversations can be marked as delivered using this method. +Messages for both user & group conversations can be marked as read using this method. Ideally, you would like to mark all the messages as delivered for any conversation when the user opens the chat window for that conversation. This includes two scenarios: -1. **When the list of messages for the conversation is fetched**: In this case you need to obtain the last message in the list of messages and pass the message to the `markAsDelivered()` method. -2. **When the user is on the chat window and a real-time message is received:** In this case you need to obtain the message and pass it to the `markAsDelivered()` method. +1. **When the list of messages for the conversation is fetched**: In this case you need to obtain the last message in the list of messages and pass the message ID of that message to the markAsDelivered() method. +2. **When the user is on the chat window and a real-time message is received:** In this case you need to obtain the message ID of the message and pass it to the markAsDelivered() method. -This method will mark all the messages before the message specified, for the conversation with `receiverId` and `receiverType` (user/group) as delivered. +This method will mark all the messages before the messageId specified, for the conversation with receiverId and receiverType(user/group) as read. In case you would like to be notified of an error if the receipts fail to go through you can use `markAsDelivered()` method with the callbacks as shown below: @@ -83,9 +31,9 @@ In case you would like to be notified of an error if the receipts fail to go thr ```dart CometChat.markAsDelivered(message, onSuccess: (String unused) { - debugPrint("markAsDelivered : $unused "); +debugPrint("markAsDelivered : $unused "); }, onError: (CometChatException e) { - debugPrint("markAsDelivered unsuccessful : ${e.message} "); +debugPrint("markAsDelivered unsuccessful : ${e.message} "); }); ``` @@ -93,40 +41,21 @@ CometChat.markAsDelivered(message, onSuccess: (String unused) { - -**On Success** — A `String` message confirming the message has been marked as delivered: - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | `String` | Success confirmation message | `"markAsDelivered success"` | - - - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | `String` | Human-readable error message | `"Failed to mark the message as delivered."` | -| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ## Mark Messages as Read *In other words, as a recipient, how do I inform the sender I've read a message?* -You can mark the messages for a particular conversation as read using the `markAsRead()` method. This method takes a `BaseMessage` object as input. +You can mark the messages for a particular conversation as read using the `markAsRead()` method. This method takes the below parameters as input: + +Messages for both user and group conversations can be marked as read using this method. + +The message object takes the below parameters as input: -| Parameter | Type | Description | -| --- | --- | --- | -| `baseMessage` | [`BaseMessage`](/sdk/reference/messages#basemessage) | The message object to mark as read. All messages before this message in the conversation will be marked as read. | -| `onSuccess` | `Function(String)` | Callback triggered on success with a confirmation string. | -| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | +| Parameter | Information | +| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| id | The ID of the message above which all the messages for a particular conversation are to be marked as read. | +| receiverUid | In a one-to-one conversation, the message's sender UID will be the receipt's receiver ID. In a group conversation, the message's receiver ID will be the receipt's receiver ID. | +| receiverType | Type of the receiver. Could be either of the two values (user or group). | Messages for both user and group conversations can be marked as read using this method. @@ -166,29 +95,6 @@ CometChat.markAsRead(message, onSuccess: (String unused) { - -**On Success** — A `String` message confirming the message has been marked as read: - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | `String` | Success confirmation message | `"markAsRead success"` | - - - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | `String` | Human-readable error message | `"Failed to mark the message as read."` | -| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ## Mark Messages as Unread The Mark as Unread feature allows users to designate specific messages or conversations as unread, even if they have been previously viewed. @@ -199,72 +105,86 @@ This feature is valuable for users who want to revisit and respond to important You can mark the messages for a particular conversation as unread using the `markMessageAsUnread()` method. -| Parameter | Type | Description | -| --- | --- | --- | -| `baseMessage` | [`BaseMessage`](/sdk/reference/messages#basemessage) | To mark a message as unread, pass a non-null `BaseMessage` instance. All messages below that message in the conversation will contribute to the unread messages count. | -| `onSuccess` | `Function(Conversation)` | Callback triggered on success with the updated `Conversation` object containing the new unread count. | -| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | - -Example: When User B sends User A a total of 10 messages, and User A invokes the `markMessageAsUnread()` method on the fifth message, all messages located below the fifth message within the conversation list will be designated as unread. This results in a notification indicating there are 5 unread messages in the conversation list. +| Parameter | Information | +| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| message | To mark a message as unread, pass a non-null `BaseMessage` instance to the `markMessageAsUnread()` function. All messages below that message in the conversation will contribute to the unread messages count. Example : When User B sends User A a total of 10 messages, and User A invokes the `markMessageAsUnread()` method on the fifth message, all messages located below the fifth message within the conversation list will be designated as unread. This results in a notification indicating there are 5 unread messages in the conversation list. | ```dart CometChat.markMessageAsUnread( - message, - onSuccess: (Conversation conversation) { - debugPrint("markMessageAsUnread : $conversation"); - }, - onError: (CometChatException error) { - debugPrint("markMessageAsUnread unsuccessful : $error"); - }, -); + message, + onSuccess: (success) { + debugPrint("markMessageAsUnread : $success"); + }, + onError: (error) { + debugPrint("markMessageAsUnread unsuccessful : $error"); + }, + ); ``` - -**On Success** — A `Conversation` object with the updated unread message count: +## Mark Conversation as Delivered - +*In other words, how do I mark an entire conversation as delivered?* -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `conversationId` | `String` | Unique identifier for the conversation | `"user_superhero1"` | -| `conversationType` | `String` | Type of conversation | `"user"` or `"group"` | -| `unreadMessageCount` | `int` | Updated unread message count | `5` | -| `lastMessage` | `BaseMessage` | The last message in the conversation | Message object | +You can mark an entire conversation as delivered using the `markConversationAsDelivered()` method. This is useful when you want to signal that all messages in a conversation have been delivered to the recipient. - + + +```dart +CometChat.markConversationAsDelivered( + conversation, + onSuccess: (success) { + debugPrint("markConversationAsDelivered : $success"); + }, + onError: (error) { + debugPrint("markConversationAsDelivered unsuccessful : $error"); + }, + ); +``` + + + + + +## Mark Conversation as Read + +*In other words, how do I mark an entire conversation as read?* - +You can mark an entire conversation as read using the `markConversationAsRead()` method. This updates the status of all messages in the conversation to "read". - + + +```dart +CometChat.markConversationAsRead( + conversation, + onSuccess: (success) { + debugPrint("markConversationAsRead : $success"); + }, + onError: (error) { + debugPrint("markConversationAsRead unsuccessful : $error"); + }, + ); +``` -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | `String` | Human-readable error message | `"Failed to mark the message as unread."` | -| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | + - + ## Receive Delivery & Read Receipts *In other words, as a recipient, how do I know when a message I sent has been delivered or read by someone?* -### Real-time Events - -Register a `MessageListener` to receive delivery and read receipt events. +### Real-time events -| Callback | Description | -| --- | --- | -| `onMessagesDelivered` | Message delivered to a user | -| `onMessagesRead` | Message read by a user | -| `onMessagesDeliveredToAll` | Group message delivered to all members | -| `onMessagesReadByAll` | Group message read by all members | +1. `onMessagesDelivered()` - This event is triggered when a message is delivered to a user. +2. `onMessagesRead()` - This event is triggered when a message is read by a user. +3. `onMessagesDeliveredToAll()` - This event is triggered when a group message is delivered to all members of the group. This event is only for Group conversations. +4. `onMessagesReadByAll()` - This event is triggered when a group message is read by all members of the group. This event is only for Group conversations. @@ -299,29 +219,17 @@ void onMessagesReadByAll(MessageReceipt messageReceipt) { - -Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. +You will receive events in the form of `MessageReceipt` objects. The message receipt contains the following parameters: -```dart -@override -void dispose() { - CometChat.removeMessageListener("listenerId"); - super.dispose(); -} -``` - - -You will receive events in the form of [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) objects. The message receipt contains the following parameters: - -| Parameter | Type | Description | -| -------------- | ---- | -------------------------------------------------------------------------------------------------------------------- | -| `messageId` | `int` | The ID of the message prior to which all the messages for that particular conversation have been marked as read. | -| `sender` | [`User`](/sdk/reference/entities#user) | User object containing the details of the user who has marked the message as read. | -| `receiverId` | `String` | Id of the receiver whose conversation has been marked as read. | -| `receiverType` | `String` | Type of the receiver (user/group) | -| `receiptType` | `String` | Type of the receipt (read/delivered) | -| `deliveredAt` | `DateTime` | The timestamp of the time when the message was delivered. This will only be present if the receiptType is delivered. | -| `readAt` | `DateTime` | The timestamp of the time when the message was read. This will only be present when the receiptType is read. | +| Parameter | Information | +| -------------- | -------------------------------------------------------------------------------------------------------------------- | +| `messageId` | The ID of the message prior to which all the messages for that particular conversation have been marked as read. | +| `sender` | User object containing the details of the user who has marked the message as read. | +| `receiverId` | Id of the receiver whose conversation has been marked as read. | +| `receiverType` | Type of the receiver (user/group) | +| `receiptType` | Type of the receipt (read/delivered) | +| `deliveredAt` | The timestamp of the time when the message was delivered. This will only be present if the receiptType is delivered. | +| `readAt` | The timestamp of the time when the message was read. This will only be present when the receiptType is read. | ### Missed Receipts @@ -329,25 +237,25 @@ You will receive message receipts when you load offline messages. While fetching However, for a group message, if you wish to fetch the `deliveredAt` and `readAt` fields of individual member of the group you can use the below-described method. -## Receipt History for a Single Message +### Receipt History for a Single Message -To fetch the message receipts, you can use the `getMessageReceipts()` method. This is useful for group messages to see which members have received/read the message. - -| Parameter | Type | Description | -| --- | --- | --- | -| `messageId` | `int` | The ID of the message for which receipts are to be fetched. | -| `onSuccess` | `Function(List)` | Callback triggered on success with a list of `MessageReceipt` objects. | -| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | +In order to fetch the message receipts, you can use the `getMessageReceipts()` method. ```dart -int messageId = 10101; +private int messageId = 10101; -CometChat.getMessageReceipts(messageId, onSuccess: (List messageReceipts) { - debugPrint("Message receipts fetched: $messageReceipts"); -}, onError: (CometChatException e) { - debugPrint("Error fetching receipts: ${e.message}"); +CometChat.getMessageReceipts(messageId, new CometChat.CallbackListener>() { +@Override + public void onSuccess(List messageReceipts) { + // Handle message receipts +} + +@Override + public void onError(CometChatException e) { + // Handle error +} }); ``` @@ -355,36 +263,7 @@ CometChat.getMessageReceipts(messageId, onSuccess: (List message - -**On Success** — A `List` containing receipt details for each group member: - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `messageId` | `int` | The ID of the message | `10101` | -| `sender` | `User` | User who triggered the receipt | User object | -| `receiverId` | `String` | ID of the receiver | `"superhero1"` | -| `receiverType` | `String` | Type of receiver | `"user"` or `"group"` | -| `receiptType` | `String` | Type of receipt | `"delivered"` or `"read"` | -| `deliveredAt` | `DateTime` | Timestamp when delivered | `DateTime` object | -| `readAt` | `DateTime` | Timestamp when read | `DateTime` object | - - - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | `String` | Human-readable error message | `"Failed to fetch message receipts."` | -| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - -You will receive a list of `MessageReceipt` objects in the `onSuccess()` callback. +You will receive a list of `MessageReceipt` objects in the `onSuccess()` method. @@ -395,24 +274,7 @@ The following features will be available only if the **Enhanced Messaging Status * `deliveredAt` field in a group message, * `readAt` field in a group message. * `markMessageAsUnread` method. +* `markConversationAsDelivered` method. +* `markConversationAsRead` method. - ---- - -## Next Steps - - - - Handle incoming messages in real-time - - - Show when users are typing - - - Fetch conversation list with unread counts - - - Complete reference for all SDK event listeners - - diff --git a/sdk/flutter/edit-message.mdx b/sdk/flutter/edit-message.mdx index f9f54efde..76ae225c6 100644 --- a/sdk/flutter/edit-message.mdx +++ b/sdk/flutter/edit-message.mdx @@ -1,72 +1,26 @@ --- -title: "Edit Message" -sidebarTitle: "Edit Message" -description: "Edit text and custom messages using the CometChat Flutter SDK." +title: "Edit A Message" --- -{/* TL;DR for Agents and Quick Reference */} - -| Field | Value | -| --- | --- | -| Key Classes | `TextMessage`, `CustomMessage`, `BaseMessage` | -| Key Methods | `CometChat.editMessage()` | -| Listener Events | `onMessageEdited` | -| Prerequisites | SDK initialized, user logged in | -```dart -// Edit a text message -TextMessage updatedMessage = TextMessage( - text: "Updated message text", - receiverUid: "receiver_uid", - receiverType: CometChatReceiverType.user, - type: CometChatMessageType.text, -); -updatedMessage.id = originalMessageId; - -await CometChat.editMessage(updatedMessage, - onSuccess: (BaseMessage message) { - debugPrint("Message edited: $message"); - }, - onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); - }, -); - -// Listen for real-time edits -CometChat.addMessageListener("edits_listener", MessageListener( - onMessageEdited: (BaseMessage message) { - debugPrint("Message was edited: ${message.id}"); - }, -)); - -// Remove listener when done -CometChat.removeMessageListener("edits_listener"); -``` - - - -Editing a message is straightforward. Receiving edit events has two parts: +While editing a message is straightforward, receiving events for edited messages with CometChat has two parts: -1. Adding a listener for [real-time edits](#real-time-message-edit-events) when your app is running -2. Fetching [missed edits](#missed-message-edit-events) when your app was offline +1. Adding a listener to receive [real-time message edit events](/sdk/flutter/edit-message#real-time-message-edit-events) when your app is running +2. Calling a method to retrieve [missed message edit events](/sdk/flutter/edit-message#missed-message-edit-events) when your app was not running ## Edit a Message -Use `editMessage()` with a [`TextMessage`](/sdk/reference/messages#textmessage) or [`CustomMessage`](/sdk/reference/messages#custommessage) object. Set the message ID using the `id` property. +*In other words, as a sender, how do I edit a message?* -| Parameter | Type | Description | -| --- | --- | --- | -| `message` | `BaseMessage` | The message object to edit. Must be a `TextMessage` or `CustomMessage`. Set the `id` property to the ID of the message to edit. | -| `onSuccess` | `Function(BaseMessage)` | Callback triggered on success with the edited message object. | -| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | +In order to edit a message, you can use the `editMessage()` method. This method takes an object of the `BaseMessage` class. At the moment, you are only allowed to edit `TextMessage` and `CustomMessage`. Thus, the `BaseMessage` object must either be a Text or a Custom Message. ### Add/Update Tags -Use `tags` to update tags when editing. New tags replace existing ones. +While editing a message, you can update the tags associated with the Message. You can use the `setTags()` method to do so. The tags added while editing a message will replace the tags set when the message was sent. - + ```dart List tags = []; tags.add("pinned"); @@ -75,18 +29,9 @@ textMessage.tags = tags; - -```dart -List tags = []; -tags.add("pinned"); -customMessage.tags = tags; -``` - - - -Once the message object is ready, call `editMessage()`. +Once the message object is ready, you can use the `editMessage()` method and pass the message object to it. @@ -112,103 +57,7 @@ await CometChat.editMessage(updatedMessage, - -**On Success** — A `BaseMessage` object containing all details of the edited message: - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `401` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#edit-message-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `"cometchat-uid-1"` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554750` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#edit-message-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `1745554800` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554800` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"The message could not be modified."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - -The edited message object is returned with `editedAt` (timestamp) and `editedBy` (UID of editor) fields set. - -The `editMessage()` method returns a [`BaseMessage`](/sdk/reference/messages#basemessage) object (or a subclass like [`TextMessage`](/sdk/reference/messages#textmessage)). - -Relevant fields to access on the returned message: - -| Field | Property | Return Type | Description | -|-------|----------|-------------|-------------| -| editedAt | `editedAt` | `int` | Timestamp when the message was edited | -| editedBy | `editedBy` | `String` | UID of the user who edited the message | +The object of the edited message will be returned in the `onSuccess()`callback method of the listener. The message object will contain the `editedAt` field set with the timestamp of the time the message was edited. This will help you identify if the message was edited while iterating through the list of messages. The `editedBy` field is also set to the `UID` of the user who edited the message. By default, CometChat allows certain roles to edit a message. @@ -221,7 +70,9 @@ By default, CometChat allows certain roles to edit a message. ## Real-time Message Edit Events -Use `onMessageEdited` in `MessageListener` to receive real-time edit events. +*In other words, as a recipient, how do I know when someone has edited their message when my app is running?* + +In order to receive real-time events for message being edited, you need to override the `onMessageEdited()` method of the `MessageListener` class. @@ -241,56 +92,21 @@ void onMessageEdited(BaseMessage message) { - -Always remove message listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. - -```dart -@override -void dispose() { - CometChat.removeMessageListener("listenerId"); - super.dispose(); -} -``` - - -The `onMessageEdited` callback receives a [`BaseMessage`](/sdk/reference/messages#basemessage) object with the `editedAt` and `editedBy` fields set. - -Relevant fields to access on the returned message: +## Missed Message Edit Events -| Field | Property | Return Type | Description | -|-------|----------|-------------|-------------| -| editedAt | `editedAt` | `int` | Timestamp when the message was edited | -| editedBy | `editedBy` | `String` | UID of the user who edited the message | +*In other words, as a recipient, how do I know when someone edited their message when my app was not running?* -## Missed Message Edit Events +When you retrieve the list of previous messages, for the message that was edited, the `editedAt` and the `editedBy` fields will be set. Also, for example, if the total number of messages for a conversation is 100, and the message with message ID 50 was edited. Now the message with ID 50 will have the `editedAt` and the `editedBy` fields set whenever it is pulled from the history. Also, the 101st message will be an `Action` message informing you that the message with ID 50 has been edited.. -When fetching message history, edited messages have `editedAt` and `editedBy` fields set. Additionally, an [`Action`](/sdk/reference/messages#action) message is created when a message is edited. +For the message edited event, in the `Action` object received, the following fields can help you get the relevant information- -The [`Action`](/sdk/reference/messages#action) object contains: -- `action` — `edited` -- `actionOn` — Updated message object with the edited details -- `actionBy` — User object containing the details of the user who has edited the message -- `actionFor` — User/group object having the details of the receiver to which the message was sent +1. `action` - `edited` +2. `actionOn` - Updated message object with the edited details. +3. `actionBy` - User object containing the details of the user who has edited the message. +4. `actionFor` - User/group object having the details of the receiver to which the message was sent. -You must be the message sender or a group admin/moderator to edit a message. - ---- +In order to edit a message, you need to be either the sender of the message or the admin/moderator of the group in which the message was sent. -## Next Steps - - - - Learn how to delete messages from conversations - - - Send text, media, and custom messages - - - Handle incoming messages in real-time - - - Create and manage message threads - - + diff --git a/sdk/flutter/extensions-overview.mdx b/sdk/flutter/extensions-overview.mdx index 5dcf22fb9..29c1774bd 100644 --- a/sdk/flutter/extensions-overview.mdx +++ b/sdk/flutter/extensions-overview.mdx @@ -1,144 +1,4 @@ --- title: "Extensions" -sidebarTitle: "Extensions" -description: "Explore CometChat extensions that add enhanced functionality to your Flutter chat application" url: "/fundamentals/extensions-overview" ---- - - -**What are Extensions?** -Extensions are add-on features that extend CometChat's core functionality beyond basic messaging. - -**How to Enable:** -1. Go to [CometChat Dashboard](https://app.cometchat.com) -2. Navigate to your App → Extensions -3. Enable desired extensions -4. Extensions activate automatically on SDK initialization - -**Extension Categories:** -- **User Experience** → Pin message, Link preview, Thumbnails, Voice transcription -- **User Engagement** → Polls, Reactions, Mentions, Message translation, Stickers -- **Collaboration** → Whiteboard, Collaborative documents -- **Notifications** → Push, Email, SMS notifications -- **Moderation** → Content filtering, Profanity detection -- **Security** → Disappearing messages, End-to-end encryption - -**Full Extension List:** [Extensions Overview](/fundamentals/extensions-overview) - - -Extensions extend CometChat's core functionality, adding enhanced features to your chat application. They help you build a complete chat experience beyond basic voice, video, and text messaging. - -## How Extensions Work - -Extensions are enabled through the CometChat Dashboard and automatically integrate with your Flutter application upon SDK initialization and successful login. Once enabled, extension features become available without additional SDK configuration. - -## Enabling Extensions - - - - Log in to the [CometChat Dashboard](https://app.cometchat.com) and select your application. - - - Go to the Extensions section in your app settings. - - - Toggle on the extensions you want to use in your application. - - - Extensions activate automatically when your Flutter app initializes the CometChat SDK. - - - -## Available Extension Categories - -### User Experience - -Extensions that improve the messaging experience: - -| Extension | Description | -| --- | --- | -| Pin Message | Allow users to pin important messages | -| Link Preview | Automatically generate previews for shared links | -| Rich Media Preview | Enhanced media previews for images and videos | -| Thumbnail Generation | Auto-generate thumbnails for media files | -| Voice Transcription | Convert voice messages to text | -| Save Message | Let users bookmark messages for later | - -### User Engagement - -Extensions that increase user interaction: - -| Extension | Description | -| --- | --- | -| Polls | Create and vote on polls within chats | -| Reactions | Add emoji reactions to messages | -| Mentions | Tag users in messages with @mentions | -| Message Translation | Translate messages to different languages | -| Smart Reply | AI-powered reply suggestions | -| Stickers | Send stickers in conversations | -| Giphy/Tenor | Search and share GIFs | - -### Collaboration - -Extensions for team collaboration: - -| Extension | Description | -| --- | --- | -| Collaborative Whiteboard | Real-time whiteboard for visual collaboration | -| Collaborative Document | Edit documents together in real-time | - -### Notifications - -Extensions for alerting users: - -| Extension | Description | -| --- | --- | -| Push Notifications | Mobile and web push alerts | -| Email Notifications | Email alerts for missed messages | -| SMS Notifications | Text message alerts | - -### Moderation - -Extensions for content safety: - -| Extension | Description | -| --- | --- | -| Profanity Filter | Automatically filter inappropriate content | -| Image Moderation | Detect and filter inappropriate images | -| Data Masking | Mask sensitive information in messages | - -### Security - -Extensions for enhanced security: - -| Extension | Description | -| --- | --- | -| Disappearing Messages | Messages that auto-delete after a set time | -| End-to-End Encryption | Secure message encryption | - -## Extension Support in UI Kit - -If you're using CometChat UI Kit for Flutter, many extensions are automatically supported and rendered in the UI. Extension features will only be available if they are supported by the CometChat UI Kit. - - -For detailed information on each extension, including configuration options and usage, visit the [Extensions Overview](/fundamentals/extensions-overview) in the fundamentals documentation. - - ---- - -## Next Steps - - - - Explore all available SDK resources and documentation - - - Configure webhooks for real-time event notifications - - - Understand API rate limits and quotas - - - Handle real-time events in your application - - +--- \ No newline at end of file diff --git a/sdk/flutter/flag-message.mdx b/sdk/flutter/flag-message.mdx index a2f8d6530..02b71550c 100644 --- a/sdk/flutter/flag-message.mdx +++ b/sdk/flutter/flag-message.mdx @@ -1,48 +1,11 @@ --- title: "Flag Message" -sidebarTitle: "Flag Message" -description: "Learn how to flag and report inappropriate messages for moderation review in your Flutter application using CometChat SDK." --- -{/* TL;DR for Agents and Quick Reference */} - - -```dart -// Get available flag reasons -CometChat.getFlagReasons( - onSuccess: (List reasons) { - debugPrint("Flag reasons: $reasons"); - }, - onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); - }, -); - -// Flag a message -FlagDetail flagDetail = FlagDetail( - reasonId: "spam", - remark: "Optional additional context", -); - -CometChat.flagMessage(messageId, flagDetail, - onSuccess: (String response) { - debugPrint("Message flagged: $response"); - }, - onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); - }, -); -``` - - ## Overview Flagging messages allows users to report inappropriate content to moderators or administrators. When a message is flagged, it appears in the [CometChat Dashboard](https://app.cometchat.com) under **Moderation > Flagged Messages** for review. - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [Dashboard](https://app.cometchat.com) - - For a complete understanding of how flagged messages are reviewed and managed, see the [Flagged Messages](/moderation/flagged-messages) documentation. @@ -88,7 +51,7 @@ Before flagging a message, retrieve the list of available flag reasons configure print("Flag reasons fetched: $reasons"); // Use reasons to populate your report dialog UI for (var reason in reasons) { - print("Reason ID: ${reason.id}, Name: ${reason.name}"); + print("Reason ID: ${reason.id}, Title: ${reason.title}"); } }, onError: (CometChatException e) { @@ -105,21 +68,8 @@ The response is a list of `FlagReason` objects containing: | Property | Type | Description | |----------|------|-------------| -| `id` | `String` | Unique identifier for the reason | -| `name` | `String` | Display name for the reason | -| `description` | `String?` | Optional description of the reason | -| `createdAt` | `DateTime?` | Timestamp when the reason was created | -| `updatedAt` | `DateTime?` | Timestamp when the reason was last updated | - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | `String` | Human-readable error message | `"Failed to fetch flag reasons."` | -| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | - - +| id | String | Unique identifier for the reason | +| title | String | Display text for the reason | ## Flag a Message @@ -130,10 +80,9 @@ To flag a message, use the `flagMessage()` method with the message ID and a `Fla ```dart int messageId = 123; // ID of the message to flag - FlagDetail flagDetail = FlagDetail( - reasonId: "spam", // Required: ID from getFlagReasons() - remark: "This message contains promotional content", // Optional - ); + FlagDetail flagDetail = FlagDetail() + ..reasonId = "spam" // Required: ID from getFlagReasons() + ..remark = "This message contains promotional content"; // Optional CometChat.flagMessage( messageId, @@ -153,29 +102,18 @@ To flag a message, use the `flagMessage()` method with the message ID and a `Fla | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `messageId` | `int` | Yes | The ID of the message to flag | -| `flagDetail` | `FlagDetail` | Yes | Contains flagging details | -| `flagDetail.reasonId` | `String` | Yes | ID of the flag reason (from `getFlagReasons()`) | -| `flagDetail.remark` | `String?` | No | Additional context or explanation from the user | - - -**On Success** — A `String` message confirming the message has been flagged: - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | `String` | Success confirmation message | `"Message 123 has been flagged successfully."` | +| messageId | int | Yes | The ID of the message to flag | +| flagDetail | FlagDetail | Yes | Contains flagging details | +| flagDetail.reasonId | String | Yes | ID of the flag reason (from `getFlagReasons()`) | +| flagDetail.remark | String | No | Additional context or explanation from the user | - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | `String` | Human-readable error message | `"Failed to flag the message."` | -| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | +### Response - +```json +{ + "message": "Message {id} has been flagged successfully." +} +``` ## Complete Example @@ -216,10 +154,12 @@ Here's a complete implementation showing how to build a report message flow: }) async { final completer = Completer(); - FlagDetail flagDetail = FlagDetail( - reasonId: reasonId, - remark: remark, - ); + FlagDetail flagDetail = FlagDetail() + ..reasonId = reasonId; + + if (remark != null) { + flagDetail.remark = remark; + } CometChat.flagMessage( messageId, @@ -257,22 +197,3 @@ Here's a complete implementation showing how to build a report message flow: ``` - ---- - -## Next Steps - - - - Handle incoming messages and real-time events - - - Remove messages from conversations - - - Automate content moderation with AI - - - Track message delivery and read status - - diff --git a/sdk/flutter/v5/flutter-overview.mdx b/sdk/flutter/flutter-overview.mdx similarity index 100% rename from sdk/flutter/v5/flutter-overview.mdx rename to sdk/flutter/flutter-overview.mdx diff --git a/sdk/flutter/group-add-members.mdx b/sdk/flutter/group-add-members.mdx index 36b38943c..e125be98b 100644 --- a/sdk/flutter/group-add-members.mdx +++ b/sdk/flutter/group-add-members.mdx @@ -1,57 +1,16 @@ --- title: "Add Members To A Group" -sidebarTitle: "Add Members" -description: "Learn how to add members to a group, receive real-time member added events, and handle missed events using the CometChat Flutter SDK." --- - -```dart -// Add members to a group -List members = [ - GroupMember.fromUid(uid: "UID", scope: CometChatMemberScope.participant, name: "User 1"), -]; - -CometChat.addMembersToGroup( - guid: "GUID", - groupMembers: members, - onSuccess: (Map result) { - debugPrint("Members added: $result"); - }, - onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); - }, -); - -// Listen for member added events -class MyGroupListener with GroupListener { - @override - void onMemberAddedToGroup(Action action, User addedby, User userAdded, Group addedTo) { - debugPrint("Member added: ${userAdded.name}"); - } -} -CometChat.addGroupListener("listenerId", MyGroupListener()); -``` - - -Add users to a group programmatically. Only admins and moderators can add members. The added users receive a notification and are immediately part of the group. ## Add Members to Group - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - - -Use `addMembersToGroup()` to add members to a [Group](/sdk/flutter/retrieve-groups). +You can add members to the group using the `addMembersToGroup()` method. This method takes the below parameters: -| Parameter | Type | Description | -|-----------|------|-------------| -| `guid` | `String` | The GUID of the group to add members to | -| `groupMembers` | `List` | List of [GroupMember](/sdk/reference/entities#groupmember) objects (UID and scope required) | -| `onSuccess` | `Function(Map)` | Callback with a map of UIDs to result status | -| `onError` | `Function(CometChatException)` | Callback triggered on failure | - -In order to add members, you need to create an object of the `GroupMember` class. The `UID` and the `scope` of the `GroupMember` are mandatory. +1. `GUID` - GUID of the group users are to be added to. +2. `List members` - This is a list of `GroupMember` objects. In order to add members, you need to create an object of the `GroupMember` class. The `UID` and the scope of the `GroupMember` are mandatory. +3. `List bannedMembers` - This is the list of `UIDs` that need to be banned from the group. This can be set to `null` if there are no members to be banned. @@ -79,39 +38,21 @@ CometChat.addMembersToGroup( guid: conversationWith, - -**On Success** — A `Map` where each key is the UID of the user and the value is either `"success"` or an error message: - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `cometchat-uid-3` | string | Result for the first member added | `"success"` | -| `cometchat-uid-4` | string | Result for the second member added | `"success"` | - - - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified group does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid group GUID."` | - - - -In the `onSuccess()` callback, you will receive a `Map` which will contain the `UID` of the users and the value will either be `success` or an error message describing why the operation to add the user to the group failed. +In the `onSuccess()` method , you will receive a Map which will contain the `UID` of the users and the value will either be `success` or an error message describing why the operation to add the user to the group or ban the user failed. ## Real-Time Group Member Added Events +*In other words, as a member of a group, how do I know when someone is added to the group when my app is running?* + + When a group member is added by another member, this event is triggered. When a user joins a group on their own, the joined event is triggered. + -Implement `onMemberAddedToGroup()` in `GroupListener` to receive real-time notifications when members are added. +To receive real-time events whenever a new member is added to a group, you need to implement the `onMemberAddedToGroup()` methods of the `GroupListener` class. + +* `onMemberAddedToGroup()` - This method is triggered when any user is added to the group so that the logged-in user is informed of the other members added to the group. @@ -130,49 +71,15 @@ void onMemberAddedToGroup(Action action, User addedby, User userAdded, Group add -| Callback Parameter | Type | Description | -|--------------------|------|-------------| -| `action` | `Action` | The action object containing details of the event | -| `addedby` | `User` | The user who added the member | -| `userAdded` | `User` | The user who was added to the group | -| `addedTo` | `Group` | The group the member was added to | +## Member Added to Group event in Message History - -Always remove group listeners when they're no longer needed (e.g., in `dispose()`). Failing to remove listeners can cause memory leaks and duplicate event handling. +*In other words, as a member of a group, how do I know when someone is added to the group when my app is not running?* -```dart -CometChat.removeGroupListener("group_Listener_id"); -``` - - -## Missed Member Added Events - -When you retrieve the list of previous messages, if a member has been added to any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. +When you retrieve the list of previous messages if a member has been added to any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. -For the group member added event, in the `Action` object received, the following fields can help you get the relevant information: - -| Field | Value/Type | Description | -|-------|------------|-------------| -| `action` | `"added"` | The action type | -| `actionOn` | [User](/sdk/reference/entities#user) | The user who was added | -| `actionBy` | [User](/sdk/reference/entities#user) | The user who added the member | -| `actionFor` | [Group](/sdk/reference/entities#group) | The group the member was added to | - ---- +For the group member added event, in the `Action` object received, the following fields can help you get the relevant information- -## Next Steps - - - - Remove or ban members from a group - - - Promote or demote group members - - - Fetch the list of members in a group - - - Allow users to join public or password-protected groups - - +1. `action` - `added` +2. `actionOn` - User object containing the details of the user who was added to the group +3. `actionBy` - User object containing the details of the user who added the member to the group +4. `actionFor` - Group object containing the details of the group to which the member was added diff --git a/sdk/flutter/group-change-member-scope.mdx b/sdk/flutter/group-change-member-scope.mdx index 0420f4fdb..412b54b93 100644 --- a/sdk/flutter/group-change-member-scope.mdx +++ b/sdk/flutter/group-change-member-scope.mdx @@ -1,37 +1,12 @@ --- title: "Change Member Scope" -sidebarTitle: "Change Scope" -description: "Learn how to change group member roles (admin, moderator, participant) and receive real-time scope change events using the CometChat Flutter SDK." --- - -```dart -// Change member scope to admin -CometChat.updateGroupMemberScope( - guid: "GROUP_ID", - uid: "USER_ID", - scope: CometChatMemberScope.admin, - onSuccess: (String message) => debugPrint("Scope updated: $message"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Listen for scope change events -CometChat.addGroupListener("listener_id", GroupListenerImpl()); - -// Scopes: CometChatMemberScope.admin, .moderator, .participant -``` - - -Promote or demote group members between admin, moderator, and participant roles. Only admins can change member scopes, and only the group owner can change admin scopes. ## Change Scope of a Group Member -Use `updateGroupMemberScope()` to change a member's role within a [Group](/sdk/reference/entities#group). - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +In order to change the scope of a group member, you can use the `changeGroupMemberScope()`. @@ -40,12 +15,12 @@ String UID = "UID"; String GUID = "GUID"; String scope = CometChatMemberScope.admin; -CometChat.updateGroupMemberScope(guid: GUID, uid: UID, scope: scope, +CometChat.updateGroupMemberScope(guid: GUID, uid: UID,scope: scope, onSuccess: (String message) { - debugPrint("Group Member Scope Changed Successfully : $message"); + debugPrint("Group Member Scope Changed Successfully : $message"); }, onError: (CometChatException e) { - debugPrint("Group Member Scope Change failed : ${e.message}"); + debugPrint("Group Member Scope Change failed : ${e.message}"); }); ``` @@ -55,42 +30,19 @@ CometChat.updateGroupMemberScope(guid: GUID, uid: UID, scope: scope, This method takes the below parameters: -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `guid` | `String` | The GUID of the group for which the member's scope needs to be changed | -| `uid` | `String` | The UID of the member whose scope you would like to change | -| `scope` | `String` | The updated scope: `CometChatMemberScope.admin`, `CometChatMemberScope.moderator`, or `CometChatMemberScope.participant` | -| `onSuccess` | `Function(String)` | Callback triggered on successful scope change | -| `onError` | `Function(CometChatException)` | Callback triggered on error | +| Parameter | Description | +| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `UID` | The UID of the member whose scope you would like to change | +| `GUID` | The GUID of the group for which the member's scope needs to be changed | +| `scope` | the updated scope of the member. This can be either of the 3 values: 1.`CometChatMemberScope.`*admin* (admin) 2.`CometChatMemberScope.`*moderator* (moderator) 3.`CometChatMemberScope.`*participant* (participant) | The default scope of any member is `participant`. Only the **Admin** of the group can change the scope of any participant in the group. - -**On Success** — A `String` message confirming the scope change: - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"cometchat-guid-1 scope changed successfully"` | - - - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_NOT_A_MEMBER"` | -| `message` | string | Human-readable error message | `"The user is not a member of this group."` | -| `details` | string | Additional technical details | `"Cannot change scope for a user who is not a member of the group."` | - - - ## Real-Time Group Member Scope Changed Events -Implement `onGroupMemberScopeChanged()` in `GroupListener` to receive real-time notifications when a member's scope changes. +*In other words, as a member of a group, how do I know when someone's scope is changed when my app is running?* + +In order to receive real-time events whenever a group member's scope changes, you will need to override the `onGroupMemberScopeChanged()` method of the `GroupListener` class @@ -110,42 +62,17 @@ void onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, - -Always remove group listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. - -```dart -CometChat.removeGroupListener("group_Listener_id"); -``` - - ## Missed Group Member Scope Changed Events -When fetching previous messages, scope changes appear as [Action](/sdk/reference/messages#action) messages (a subclass of `BaseMessage`). +*In other words, as a member of a group, how do I know when someone's scope is changed when my app is not running?* -| Field | Value/Type | Description | -|-------|------------|-------------| -| `action` | `"scopeChanged"` | The action type | -| `actionOn` | [User](/sdk/reference/entities#user) | The user whose scope changed | -| `actionBy` | [User](/sdk/reference/entities#user) | The user who changed the scope | -| `actionFor` | [Group](/sdk/reference/entities#group) | The group | -| `oldScope` | `String` | The previous scope | -| `newScope` | `String` | The new scope | +When you retrieve the list of previous messages if a member's scope has been changed for any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. ---- +For the group member scope changed event, in the `Action` object received, the following fields can help you get the relevant information- -## Next Steps - - - - Transfer ownership of a group to another member - - - Remove or ban members from a group - - - Add new members to a group - - - Fetch the list of members in a group - - +1. `action` - `scopeChanged` +2. `actionOn` - User object containing the details of the user whos scope has been changed +3. `actionBy` - User object containing the details of the user who changed the scope of the member +4. `actionFor` - Group object containing the details of the group in which the member scope was changed +5. `oldScope` - The original scope of the member +6. `newScope` - The updated scope of the member diff --git a/sdk/flutter/v5/group-kick-member.mdx b/sdk/flutter/group-kick-member.mdx similarity index 100% rename from sdk/flutter/v5/group-kick-member.mdx rename to sdk/flutter/group-kick-member.mdx diff --git a/sdk/flutter/groups-overview.mdx b/sdk/flutter/groups-overview.mdx index 3e27ebc8c..f219e6c57 100644 --- a/sdk/flutter/groups-overview.mdx +++ b/sdk/flutter/groups-overview.mdx @@ -1,75 +1,61 @@ --- title: "Groups" sidebarTitle: "Overview" -description: "Overview of group management in the CometChat Flutter SDK including group types, member roles, and available operations." --- - +A group can be used for multiple users to communicate with each other on a particular topic or interest. -| Field | Value | -| --- | --- | -| Package | `cometchat_sdk` | -| Key Classes | `CometChat`, `Group`, `GroupMember` | -| Group Types | `public`, `private`, `password` | -| Member Roles | `owner`, `admin`, `moderator`, `participant` | -| Key Methods | `createGroup()`, `joinGroup()`, `leaveGroup()`, `deleteGroup()` | -| Prerequisites | SDK initialized, user logged in | -| Related | [Create Group](/sdk/flutter/create-group), [Join Group](/sdk/flutter/join-group), [Retrieve Groups](/sdk/flutter/retrieve-groups) | +## GUID - +- Each group is uniquely identified using a GUID. +- The GUID is typically the primary ID of the group from your database. If you do not store group information in your database, you can generate a random string for use as GUID. -Groups let users converse together in a shared space. CometChat supports three group types (public, private, password-protected) and four member roles with different permission levels. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - + +GUID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. + ## Group Types -| Type | Description | Join Behavior | -|------|-------------|---------------| -| **Public** | Open to all users | Any user can join without approval | -| **Private** | Invite-only | Users must be added by admin/moderator | -| **Password** | Protected by password | Users must provide correct password to join | - -## Member Roles - -| Role | Permissions | -|------|-------------| -| **Owner** | Full control: manage members, settings, delete group. Cannot leave without transferring ownership. | -| **Admin** | Manage members (add, kick, ban), change member scope, update group settings | -| **Moderator** | Kick and ban members, moderate content | -| **Member** | Send/receive messages, leave group | - -## Available Operations - -- [Create a Group](/sdk/flutter/create-group) — Create public, private, or password-protected groups -- [Join a Group](/sdk/flutter/join-group) — Join existing groups as a participant -- [Leave a Group](/sdk/flutter/leave-group) — Leave a group you're a member of -- [Update a Group](/sdk/flutter/update-group) — Update group name, description, icon, and settings -- [Delete a Group](/sdk/flutter/delete-group) — Permanently delete a group (owner only) -- [Transfer Ownership](/sdk/flutter/transfer-group-ownership) — Transfer group ownership to another member -- [Retrieve Groups](/sdk/flutter/retrieve-groups) — Fetch and filter the list of groups -- [Retrieve Group Members](/sdk/flutter/retrieve-group-members) — Get the member list for a group -- [Add Members](/sdk/flutter/group-add-members) — Add users to a group -- [Kick & Ban Members](/sdk/flutter/group-kick-ban-members) — Remove or ban members from a group -- [Change Member Scope](/sdk/flutter/group-change-member-scope) — Promote or demote members - ---- - -## Next Steps - - - - Create public, private, or password-protected groups - - - Join existing groups as a participant - - - Fetch and filter the list of groups - - - Get the member list for a group - - +CometChat supports three different types of groups: + +```mermaid +flowchart LR + G["Group"] --> Public + G --> Password + G --> Private + + Public -.- P1["Visible to all, anyone can join"] + Password -.- P2["Visible to all, requires password"] + Private -.- P3["Hidden, invite only"] +``` + +| Type | Visibility | Participation | +|------|-----------|---------------| +| Public | All users | Any user can choose to join | +| Password | All users | Any user with a valid password can join | +| Private | Only members | Invited users are auto-joined | + +## Member Scopes + +Once a participant joins a group, they become a member. Members remain part of the group indefinitely — to stop receiving messages, calls, and notifications, a member must be kicked, banned, or leave the group. + +```mermaid +flowchart TD + Admin["Admin (Group creator)"] + Moderator["Moderator"] + Participant["Participant (Default scope)"] + + Admin -->|Can promote or demote| Moderator + Admin -->|Can promote or demote| Participant + Moderator -->|Can promote or demote| Participant + + Admin -.- A1["Full control: manage members, update/delete group, message & call"] + Moderator -.- M1["Moderate: kick/ban participants, update group, message & call"] + Participant -.- P1["Basic: send & receive messages & calls"] +``` + +| Scope | Default | Privileges | +|-------|---------|-----------| +| Admin | Group creator | Manage all members, add members, kick & ban anyone, send & receive messages & calls, update & delete group | +| Moderator | — | Change scope of participants, kick & ban participants, update group, send & receive messages & calls | +| Participant | All other members | Send & receive messages & calls | diff --git a/sdk/flutter/interactive-messages.mdx b/sdk/flutter/interactive-messages.mdx index e13d96a3e..ce85974c3 100644 --- a/sdk/flutter/interactive-messages.mdx +++ b/sdk/flutter/interactive-messages.mdx @@ -1,77 +1,81 @@ --- title: "Interactive Messages" -sidebarTitle: "Interactive Messages" -description: "Send and receive interactive messages with embedded forms, buttons, and other UI elements using the CometChat Flutter SDK." --- - -| Field | Value | -| --- | --- | -| Key Classes | `InteractiveMessage`, `InteractionGoal`, `Interaction`, `InteractionReceipt` | -| Key Methods | `CometChat.sendInteractiveMessage()`, `CometChat.markAsInteracted()` | -| Key Constants | `InteractionGoalType.anyAction`, `InteractionGoalType.anyOf`, `InteractionGoalType.allOf`, `InteractionGoalType.none` | -| Listener Events | `onInteractiveMessageReceived`, `onInteractionGoalCompleted` | -| Prerequisites | SDK initialized, user logged in | +An `InteractiveMessage` is a specialized object that encapsulates an interactive unit within a chat message, such as an embedded form that users can fill out directly within the chat interface. This enhances user engagement by making the chat experience more interactive and responsive to user input. + +`InteractiveMessage` is a chat message with embedded interactive content. It can contain various properties: + +| Parameter | Description | +| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| receiverId | The UID or GUID of the recipient | +| receiverType | The type of the receiver to whom the message is to be sent i.e `CometChatConstants.RECEIVER_TYPE_USER` (user) or `CometChatConstants.RECEIVER_TYPE_GROUP` (group) | +| messageType | The type of the message that needs to be sent | +| interactiveData | A JSONObject holding structured data for the interactive element | +| allowSenderInteraction | A boolean determining whether the message sender can interact with the message, by default, it is set to false | +| interactionGoal | An InteractionGoal object encapsulating the intended outcome of interacting with the InteractiveMessage, by default, it is set to none | + +### Interaction + +An `Interaction` represents a user action involved with an `InteractiveMessage`. It includes: + +* `elementId`: An identifier for a specific interactive element. +* `interactedAt`: A timestamp indicating when the interaction occurred. + +### Mark as Interacted + +This method marks a message as interacted by identifying it with the provided Id. it also logs the interactive element associated with the interaction. + + + ```dart -// Create and send an interactive form message -InteractiveMessage interactiveMessage = InteractiveMessage( - interactiveData: interactiveData, // Map with form fields - receiverUid: "UID", - type: "form", - receiverType: CometChatReceiverType.user, - allowSenderInteraction: true, - interactionGoal: InteractionGoal(type: InteractionGoalType.none), -); -CometChat.sendInteractiveMessage(interactiveMessage, - onSuccess: (InteractiveMessage message) {}, - onError: (CometChatException e) {}, -); - -// Mark as interacted -CometChat.markAsInteracted(messageId, elementId, - onSuccess: (String message) {}, - onError: (CometChatException e) {}, -); - -// Listen for interactive messages -CometChat.addMessageListener("LISTENER_ID", this); -// onInteractiveMessageReceived(InteractiveMessage message) { } -// onInteractionGoalCompleted(InteractionReceipt receipt) { } +await CometChat.markAsInteracted(messageId, interactedElementId, + onSuccess: (String message ){ + //write code here for after success + + }, onError: + (CometChatException excep){ + //write code here for after err + + }); ``` - + + + + +### Goal Completion + +A key feature of `InteractiveMessage` is checking whether a user's interactions with the message meet the defined `InteractionGoal` + +| | | | +| -------------------------------- | ---------------------------------------------------------------------- | ----------------------------- | +| **Any Interaction** | The goal is considered completed if there is at least one interaction. | InteractionGoalType.anyAction | +| **Any of Specific Interactions** | The goal is achieved if any of the specified interactions occurred. | InteractionGoalType.anyOf | +| **All of Specific Interactions** | The goal is completed when all specified interactions occur. | InteractionGoalType.allOf | +| **None** | The goal is never completed | InteractionGoalType.none | + +You would be tracking every interaction users perform on an `InteractiveMessage` (captured as `Interaction` objects) and comparing those with the defined `InteractionGoal`. The completion of a goal can vary depending on the goal type: -Interactive messages embed UI elements like forms, buttons, and dropdowns directly within chat messages. Users can interact with these elements without leaving the conversation, enabling surveys, quick actions, and data collection. +This user interaction tracking mechanism provides a flexible and efficient way to monitor user engagement within an interactive chat session. By defining clear interaction goals and checking user interactions against these goals, you can manage user engagement and improve the overall chat experience in your CometChat-enabled application. - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +### InteractionGoal -## InteractiveMessage +The `InteractionGoal` represents the desired outcome of an interaction with an InteractiveMessage. It includes: -The `InteractiveMessage` class extends `BaseMessage` and represents a message with embedded interactive content. +* `elementIds`: A list of identifiers for the interactive elements. +* `type`: The type of interaction goal from the `CometChatConstants`. -| Parameter | Type | Description | Required | -| --- | --- | --- | --- | -| `receiverUid` | `String` | The UID of the user or GUID of the group | Yes | -| `receiverType` | `String` | `CometChatReceiverType.user` or `CometChatReceiverType.group` | Yes | -| `type` | `String` | Type identifier (e.g., `"form"`, `"card"`) | Yes | -| `interactiveData` | `Map` | JSON structure defining the interactive elements | Yes | -| `allowSenderInteraction` | `bool` | Whether sender can interact with the message | No (default: `false`) | -| `interactionGoal` | `InteractionGoal` | Defines when the interaction is considered complete | No (default: `none`) | -| `tags` | `List` | Tags associated with the message | No | -| `muid` | `String` | Unique message identifier for deduplication | No | -| `parentMessageId` | `int` | ID of the parent message (for threads) | No | -| `metadata` | `Map` | Custom metadata attached to the message | No | +### Sending InteractiveMessages -## Send an Interactive Message +The InteractiveMessage can be sent using the sendInteractiveMessage method of the CometChat class. The method requires an InteractiveMessage object and a CallbackListener for handling the response. -Use `CometChat.sendInteractiveMessage()` to send an interactive message. +Here is an example of how to use it: - + ```dart final Map interactiveData = { "title": "Survey", @@ -169,297 +173,19 @@ print(excep); ``` - - -The `sendInteractiveMessage()` method returns an `InteractiveMessage` object on success. - - -**On Success** — An `InteractiveMessage` object containing all details of the sent interactive message: - - - -**InteractiveMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `501` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#send-interactive-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-3"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-3"` | -| `type` | string | Type of the message | `"form"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#send-interactive-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"interactive"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `interactiveData` | object | Structured data for the interactive element | `{"title": "Survey", "formFields": [...]}` | -| `interactions` | array | List of user interactions with the message | `[]` | -| `interactionGoal` | object | Intended outcome of interacting with the message | `{"type": "none", "elementIds": []}` | -| `tags` | array | List of tags associated with the message | `[]` | -| `allowSenderInteraction` | boolean | Whether the sender can interact with the message | `true` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-3"` | -| `name` | string | Display name of the receiver | `"Kevin Hart"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to send the interactive message."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - -## Interactive Elements - -The `interactiveData` map defines the UI elements. Common element types: - -| Element Type | Description | -| --- | --- | -| `textInput` | Single or multi-line text field | -| `dropdown` | Single-select dropdown menu | -| `Select` | Multi-select checkbox group | -| `button` | Clickable button with action | - -### Text Input - -```dart -{ - "elementType": "textInput", - "elementId": "name", - "optional": false, - "label": "Name", - "maxLines": 1, // Optional: limit to single line - "placeholder": {"text": "Enter text here"} -} -``` - -### Dropdown (Single Select) - -```dart -{ - "elementType": "dropdown", - "elementId": "country", - "optional": false, - "label": "Country", - "defaultValue": "us", - "options": [ - {"label": "United States", "value": "us"}, - {"label": "United Kingdom", "value": "uk"}, - {"label": "Canada", "value": "ca"} - ] -} -``` - -### Checkbox (Multi Select) - -```dart -{ - "elementType": "Select", - "elementId": "preferences", - "optional": true, - "label": "Preferences", - "defaultValue": ["email"], - "options": [ - {"label": "Email notifications", "value": "email"}, - {"label": "SMS notifications", "value": "sms"}, - {"label": "Push notifications", "value": "push"} - ] -} -``` - -### Submit Button - -```dart -{ - "elementType": "button", - "elementId": "submitBtn", - "buttonText": "Submit", - "disableAfterInteracted": true, - "action": { - "actionType": "urlNavigation", - "url": "https://example.com/submit" - } -} -``` - -## Interaction Goals - -An `InteractionGoal` defines when the user's interaction with the message is considered complete. Use this to track engagement and trigger follow-up actions. -`InteractionGoal` Parameters: - -| Parameter | Type | Description | Required | -| --- | --- | --- | --- | -| `type` | `String` | The type of interaction goal (see goal types below) | Yes | -| `elementIds` | `List` | List of element IDs associated with this goal | No (default: `[]`) | - -| Goal Type | Constant | Description | -| --- | --- | --- | -| Any Interaction | `InteractionGoalType.anyAction` | Complete when any element is interacted with | -| Any of Specific | `InteractionGoalType.anyOf` | Complete when any of the specified elements is interacted with | -| All of Specific | `InteractionGoalType.allOf` | Complete when all specified elements are interacted with | -| None | `InteractionGoalType.none` | Never considered complete (default) | - -### Set an Interaction Goal - - - -```dart -List elementIds = ["name", "gender", "submitButton"]; -InteractionGoal interactionGoal = InteractionGoal( - type: InteractionGoalType.allOf, - elementIds: elementIds, -); - -InteractiveMessage interactiveMessage = InteractiveMessage( - interactiveData: interactiveData, - receiverUid: "cometchat-uid-3", - type: "form", - receiverType: "user", - interactionGoal: interactionGoal, -); - -CometChat.sendInteractiveMessage(interactiveMessage, - onSuccess: (InteractiveMessage message) { - debugPrint("Interactive message sent successfully: $message"); - }, - onError: (CometChatException excep) { - debugPrint("Interactive message sending failed with error: ${excep.message}"); - }, -); -``` - - -You would be tracking every interaction users perform on an `InteractiveMessage` (captured as `Interaction` objects) and comparing those with the defined `InteractionGoal`. The completion of a goal can vary depending on the goal type. - -This user interaction tracking mechanism provides a flexible and efficient way to monitor user engagement within an interactive chat session. By defining clear interaction goals and checking user interactions against these goals, you can manage user engagement and improve the overall chat experience in your CometChat-enabled application. - -## Interactions - -An `Interaction` represents a single user action on an interactive element. - -| Property | Type | Description | -| --- | --- | --- | -| `elementId` | `String` | Identifier of the interacted element | -| `interactedAt` | `DateTime` | Timestamp indicating when the interaction occurred | - -## Mark as Interacted - -Use `CometChat.markAsInteracted()` to record when a user interacts with an element. This method marks a message as interacted by identifying it with the provided ID and logs the interactive element associated with the interaction. - - - -```dart -Future recordInteraction() async { - int messageId = 0; // set to your interactive message id - String interactedElementId = ""; // set to the element id - - await CometChat.markAsInteracted( - messageId, - interactedElementId, - onSuccess: (String message) { - // after success - }, - onError: (CometChatException excep) { - // after error - }, - ); -} -``` - - - +### Event Listeners -| Parameter | Type | Description | Required | -| --- | --- | --- | --- | -| `messageId` | `int` | The ID of the interactive message | Yes | -| `interactedElementId` | `String` | The element ID that was interacted with | Yes | +CometChat SDK provides event listeners to handle real-time events related to `InteractiveMessage`. - -**On Success** — A `String` message confirming the interaction was recorded: - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"Message marked as interacted successfully."` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to mark the message as interacted."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - -## Real-Time Events - -Register a `MessageListener` to receive interactive message events. - -### Receive Interactive Messages +### On InteractiveMessage Received The `onInteractiveMessageReceived` event listener is triggered when an `InteractiveMessage` is received. +Here is an example: + ```dart @@ -482,21 +208,14 @@ void onInteractiveMessageReceived(InteractiveMessage message){ ``` + -### Interaction Goal Completed +### On Interaction Goal Completed The `onInteractionGoalCompleted` event listener is invoked when an interaction goal is achieved. -`InteractionReceipt` Properties: - -| Property | Type | Description | -| --- | --- | --- | -| `messageId` | `int` | The ID of the interactive message | -| `sender` | `User` | The user who completed the interaction | -| `receiverType` | `String` | Type of the receiver (`"user"` or `"group"`) | -| `receiverId` | `String` | UID or GUID of the receiver | -| `interactions` | `List` | List of interactions that satisfied the goal | +Here is an exampl @@ -519,61 +238,11 @@ void onInteractionGoalCompleted(InteractionReceipt receipt) { ``` + These event listeners offer your application a way to provide real-time updates in response to incoming interactive messages and goal completions, contributing to a more dynamic and responsive user chat experience. - -Always remove listeners when they're no longer needed to prevent memory leaks. - -```dart -CometChat.removeMessageListener("UNIQUE_LISTENER_ID"); -``` - - -## Allow Sender Interaction - -By default, the sender cannot interact with their own interactive message. Set `allowSenderInteraction` to `true` to enable sender interaction: - - - -```dart -InteractiveMessage interactiveMessage = InteractiveMessage( - interactiveData: interactiveData, - receiverUid: "cometchat-uid-3", - type: "form", - receiverType: "user", - allowSenderInteraction: true, -); - -CometChat.sendInteractiveMessage(interactiveMessage, - onSuccess: (InteractiveMessage message) { - debugPrint("Interactive message sent successfully: $message"); - }, - onError: (CometChatException excep) { - debugPrint("Interactive message sending failed with error: ${excep.message}"); - }, -); -``` - - - - ---- +### Usage -## Next Steps - - - - Send text, media, and custom messages - - - Listen for incoming messages in real time - - - Send ephemeral messages that aren't stored - - - Understand message types and hierarchy - - +An `InteractiveMessage` is constructed with the receiver's UID, the receiver type, the interactive type, and interactive data as a JSONObject. Once created, the `InteractiveMessage` can be sent using CometChat's `sendInteractiveMessage()` method. Incoming `InteractiveMessages` can be received and processed via CometChat's message listener framework. diff --git a/sdk/flutter/join-group.mdx b/sdk/flutter/join-group.mdx index bc2595378..41e6b00da 100644 --- a/sdk/flutter/join-group.mdx +++ b/sdk/flutter/join-group.mdx @@ -1,48 +1,12 @@ --- title: "Join A Group" -sidebarTitle: "Join Group" -description: "Learn how to join public and password-protected groups using the CometChat Flutter SDK." --- - -```dart -// Join a public group -CometChat.joinGroup("GROUP_GUID", CometChatGroupType.public, - onSuccess: (Group group) => debugPrint("Joined: ${group.name}"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Join a password-protected group -CometChat.joinGroup("GROUP_GUID", CometChatGroupType.password, - password: "group_password", - onSuccess: (Group group) => debugPrint("Joined: ${group.name}"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); -``` - -**Group types:** `CometChatGroupType.public` | `CometChatGroupType.password` | `CometChatGroupType.private` - - -Join a group to start sending and receiving messages in it. Public groups can be joined freely, password groups require the correct password, and private groups require an admin to add you (no direct join). ## Join a Group -Use `joinGroup()` to join a group. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `guid` | `String` | The GUID of the group to join | -| `groupType` | `String` | `CometChatGroupType.public`, `CometChatGroupType.password`, or `CometChatGroupType.private` | -| `password` | `String` | Required for password-protected groups (defaults to empty string) | -| `onSuccess` | `Function(Group group)?` | Callback triggered on successful join | -| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | +In order to start participating in group conversations, you will have to join a group. You can do so using the `joinGroup()` method. @@ -63,45 +27,19 @@ CometChat.joinGroup(GUID, groupType, password: password, - -**On Success** — A `Group` object containing all details of the joined group: - - - -**Group Object:** +The `joinGroup()` method takes the below parameters -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | -| `name` | string | Display name of the group | `"Hello Group!"` | -| `icon` | string | URL of the group icon | `null` | -| `description` | string | Description of the group | `null` | -| `membersCount` | number | Number of members in the group | `5` | -| `metadata` | object | Custom metadata attached to the group | `{}` | -| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | -| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | -| `createdAt` | number | Epoch timestamp when the group was created | `1745551200` | -| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | -| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | -| `tags` | array | List of tags associated with the group | `[]` | -| `type` | string | Type of the group (public, private, password) | `"public"` | -| `scope` | string | Scope of the logged-in user in the group | `"participant"` | -| `password` | string | Password for password-protected groups | `null` | -| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | +| Parameter | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `GUID` | The GUID of the group you would like to join | +| `groupType` | Type of the group. CometChat provides 3 types of groups viz. 1.CometChatGroupType.*public* (public). 2.CometChatGroupType.*password* (password) 3.CometChatGroupType.*private* (private) | +| `password` | Password is mandatory in case of a password protected group. | - +Once you have joined a group successfully, you can send and receive messages in that group. - +CometChat keeps a track of the groups joined and you do not need to join the group everytime you want to communicate in the group. -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified group does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | - - - -Once joined, you can send and receive messages in the group. CometChat tracks joined groups — you don't need to rejoin each session. Check `hasJoined` on the [`Group`](/sdk/reference/entities#group) object to verify membership. +You can identify if a group is joined using the `hasJoined` parameter in the `Group` object. ## Real-time Group Member Joined Events @@ -112,13 +50,12 @@ If a user joins any group, the members of the group receive a real-time event in ```dart -class ClassName with GroupListener { - // CometChat.addGroupListener("group_Listener_id", this); +class Class_Name with GroupListener { - @override - void onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup) { - debugPrint("onGroupMemberJoined"); - } +//CometChat.addGroupListener("group_Listener_id", this); +@override +void onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup) { + debugPrint("onGroupMemberJoined"); } ``` @@ -126,14 +63,6 @@ class ClassName with GroupListener { - -Always remove group listeners when they're no longer needed (e.g., in `dispose()`). Failing to remove listeners can cause memory leaks and duplicate event handling. - -```dart -CometChat.removeGroupListener("group_Listener_id"); -``` - - ## Missed Group Member Joined Events *In other words, as a member of a group, how do I know if someone joins the group when my app is not running?* @@ -145,22 +74,3 @@ For the group member joined event, in the `Action` object received, the followin 1. `action` - `joined` 2. `actionBy` - User object containing the details of the user who joined the group 3. `actionFor`- Group object containing the details of the group the user has joined - ---- - -## Next Steps - - - - Allow members to leave a group - - - Fetch the list of members in a group - - - Send messages to group conversations - - - Programmatically add members to a group - - diff --git a/sdk/flutter/leave-group.mdx b/sdk/flutter/leave-group.mdx index 3d57928d7..f73638b8d 100644 --- a/sdk/flutter/leave-group.mdx +++ b/sdk/flutter/leave-group.mdx @@ -1,48 +1,12 @@ --- title: "Leave A Group" -sidebarTitle: "Leave Group" -description: "Learn how to leave a group and receive real-time events when members leave using the CometChat Flutter SDK." --- - -```dart -// Leave a group -CometChat.leaveGroup("GROUP_GUID", - onSuccess: (String message) => debugPrint("Left group: $message"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Listen for group member left events -CometChat.addGroupListener("listener_id", GroupListener( - onGroupMemberLeft: (Action action, User leftUser, Group leftGroup) { - debugPrint("${leftUser.name} left ${leftGroup.name}"); - }, -)); -``` - - -Leave a group to stop receiving messages and updates from it. Once you leave, you'll need to rejoin to participate again. - - -Group owners cannot leave without first transferring ownership to another member. See [Transfer Group Ownership](/sdk/flutter/transfer-group-ownership). - ## Leave a Group -Use `leaveGroup()` to leave a group. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `guid` | `String` | The GUID of the group you would like to leave | -| `onSuccess` | `Function(String returnResponse)?` | Callback triggered on successful leave | -| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | +In order to stop receiving updates and messages for any particular joined group, you will have to leave the group using the `leaveGroup()` method. @@ -60,28 +24,9 @@ CometChat.leaveGroup(GUID, onSuccess: ( String message) { - -**On Success** — A `String` message confirming the operation: - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"cometchat-guid-1 left successfully"` | - - - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_NOT_A_MEMBER"` | -| `message` | string | Human-readable error message | `"The user is not a member of this group."` | -| `details` | string | Additional technical details | `"Cannot leave a group that you are not a member of."` | - - +| Parameter | Description | +| --------- | --------------------------------------------- | +| `GUID` | The GUID of the group you would like to leave | Once a group is left, the user will no longer receive any updates or messages pertaining to the group. @@ -109,41 +54,14 @@ void onGroupMemberLeft(Action action, User leftUser, Group leftGroup) { - -Always remove group listeners when they're no longer needed (e.g., in `dispose()`). Failing to remove listeners can cause memory leaks and duplicate event handling. - -```dart -CometChat.removeGroupListener("group_Listener_id"); -``` - - ## Missed Group Member Left Events *In other words, as a member of a group, how do I know if someone has left it when my app is not running?* -When you retrieve the list of previous messages if a member has left any group that the logged-in user is a member of, the list of messages will contain an [`Action`](/sdk/reference/messages#action) message. An `Action` message is a sub-class of [`BaseMessage`](/sdk/reference/messages#basemessage) class. +When you retrieve the list of previous messages if a member has left any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. For the group member left event, in the `Action` object received, the following fields can help you get the relevant information- 1. `action` - `left` 2. `actionBy` - User object containing the details of the user who left the group 3. `actionFor` - Group object containing the details of the group the user has left - ---- - -## Next Steps - - - - Join public or password-protected groups - - - Permanently delete a group (admin only) - - - Remove or ban members from a group - - - Fetch and filter the list of groups - - diff --git a/sdk/flutter/v5/login-listeners.mdx b/sdk/flutter/login-listeners.mdx similarity index 100% rename from sdk/flutter/v5/login-listeners.mdx rename to sdk/flutter/login-listeners.mdx diff --git a/sdk/flutter/mentions.mdx b/sdk/flutter/mentions.mdx index d837732e7..2a0ad288e 100644 --- a/sdk/flutter/mentions.mdx +++ b/sdk/flutter/mentions.mdx @@ -1,55 +1,12 @@ --- title: "Mentions" -sidebarTitle: "Mentions" -description: "Send messages with user mentions, retrieve mentioned users, and filter messages by mention metadata using the CometChat Flutter SDK." --- - -| Field | Value | -| --- | --- | -| Key Classes | `TextMessage`, `BaseMessage`, `User`, `MessagesRequest`, `MessagesRequestBuilder` | -| Key Properties | `mentionedUsers`, `hasMentionedMe` | -| Builder Methods | `mentionsWithTagInfo()`, `mentionsWithBlockedInfo()` | -| Mention Format | `<@uid:UID>` | -| Prerequisites | SDK initialized, user logged in | - -```dart -// Send a message with a mention (use <@uid:UID> format) -TextMessage textMessage = TextMessage( - text: "Hello <@uid:cometchat-uid-1>", - receiverUid: "UID", - receiverType: CometChatReceiverType.user, - type: CometChatMessageType.text, -); -CometChat.sendMessage(textMessage, onSuccess: (msg) { - debugPrint("Mentioned users: ${msg.mentionedUsers}"); -}, onError: (e) {}); - -// Get mentioned users from a message -List mentionedUsers = message.mentionedUsers; - -// Check if logged-in user was mentioned -bool wasMentioned = message.hasMentionedMe ?? false; - -// Fetch messages with mention tag info -MessagesRequest request = (MessagesRequestBuilder() - ..uid = "UID" - ..limit = 30 - ..mentionsWithTagInfo(true) -).build(); -request.fetchPrevious(onSuccess: (messages) {}, onError: (e) {}); -``` - - - -Mentions in messages enable users to refer to specific individuals within a conversation. This is done by using the `<@uid:UID>` format, where `UID` represents the user's unique identification. Mentions are a powerful tool for enhancing communication in messaging platforms. They streamline interaction by allowing users to easily engage and collaborate with particular individuals, especially in group conversations. - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +Mentions in messages enable users to refer to specific individuals within a conversation. ## Send Mentioned Messages @@ -110,120 +67,6 @@ CometChat.sendMessage(textMessage, onSuccess:(TextMessage textMessage) { - -**On Success** — A `TextMessage` object containing all details of the sent message with mentions: - - - -**TextMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `501` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-mentions-send-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-mentions-send-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `text` | string | The text content of the message | `"Hello, <@uid:cometchat-uid-1>"` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | [See below ↓](#fetch-mentions-send-mentionedusers-object) | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `moderationStatus` | string | Moderation status of the message | `null` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - ---- - - - -**`mentionedUsers` Array (per item):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the mentioned user | `"cometchat-uid-1"` | -| `name` | string | Display name of the mentioned user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to send the message."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - You can mention user in text message and media messages captions @@ -243,13 +86,6 @@ By default, the SDK will fetch all the messages irrespective of the fact that th To get a list of messages in a conversation where users are mentioned along with the tags of the mentioned users. -Relevant fields to access on returned messages and their mentioned users: - -| Field | Property | Return Type | Description | -|-------|----------|-------------|-------------| -| mentionedUsers | `mentionedUsers` | [`List`](/sdk/reference/entities#user) | Array of users mentioned in the message | -| tags (on each mentioned user) | `tags` | `List` | Tags associated with the mentioned user | - ```dart @@ -303,106 +139,10 @@ messagesRequest.fetchPrevious(onSuccess:(List messages) { - -**On Success** — A `List` containing messages with mentioned users and their tag info (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `502` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-mentions-taginfo-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-mentions-taginfo-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ## Mentions With Blocked Info To get a list of messages in a conversation where users are mentioned along with the blocked relationship of the mentioned users with the logged-in user. -Relevant fields to access on returned messages and their mentioned users: - -| Field | Property | Return Type | Description | -|-------|----------|-------------|-------------| -| mentionedUsers | `mentionedUsers` | [`List`](/sdk/reference/entities#user) | Array of users mentioned in the message | -| blockedByMe (on each mentioned user) | `blockedByMe` | `bool` | Whether the logged-in user has blocked this mentioned user | -| hasBlockedMe (on each mentioned user) | `hasBlockedMe` | `bool` | Whether this mentioned user has blocked the logged-in user | - ```dart @@ -458,100 +198,12 @@ messagesRequest.fetchPrevious(onSuccess:(List messages) { - -**On Success** — A `List` containing messages with mentioned users and their blocked relationship info (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `503` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-mentions-blockedinfo-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-mentions-blockedinfo-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ## Get Users Mentioned In a Particular Message To retrieve the list of users mentioned in the particular message, you can use the `mentionedUsers` property on any `BaseMessage`. This property will return an array containing User objects of the mentioned users, or an empty array if no users were mentioned in the message. - + ```dart message.mentionedUsers ``` @@ -560,8 +212,6 @@ message.mentionedUsers -The `mentionedUsers` property returns a `List<`[`User`](/sdk/reference/entities#user)`>` objects. - ## Check if Logged-in user has been mentioned To check if the logged-in user has been mentioned in a particular message we can use the `hasMentionedMe` property on any `BaseMessage`. This property will return a boolean value, true if the logged-in user has been mentioned, otherwise `false`. @@ -575,22 +225,3 @@ message.hasMentionedMe - ---- - -## Next Steps - - - - Send text, media, and custom messages - - - Listen for incoming messages in real time - - - Add emoji reactions to messages - - - Organize conversations with message threads - - diff --git a/sdk/flutter/v5/messaging-overview.mdx b/sdk/flutter/messaging-overview.mdx similarity index 100% rename from sdk/flutter/v5/messaging-overview.mdx rename to sdk/flutter/messaging-overview.mdx diff --git a/sdk/flutter/overview.mdx b/sdk/flutter/overview.mdx index 6789301ed..d2f57628e 100644 --- a/sdk/flutter/overview.mdx +++ b/sdk/flutter/overview.mdx @@ -1,149 +1,97 @@ --- -title: "Flutter SDK" +title: "Chat SDK" sidebarTitle: "Overview" -description: "Add real-time chat, voice, and video to your Flutter application with the CometChat SDK." --- -{/* TL;DR for Agents and Quick Reference */} - +The CometChat Chat SDK for Flutter enables real-time messaging, user management, group conversations, and more in your Flutter application. Built as a pure Dart implementation in v5, it removes the dependency on platform channels and works seamlessly across Android, iOS, Web, and desktop. -```yaml -# Install (pubspec.yaml) -cometchat_sdk: ^4.0.33 -``` + + -```dart -import 'package:cometchat_sdk/cometchat_sdk.dart'; - -// 1. Initialize (once at app startup) -AppSettings appSettings = (AppSettingsBuilder() - ..subscriptionType = CometChatSubscriptionType.allUsers - ..region = "APP_REGION" // e.g. "us", "eu", "in" - ..autoEstablishSocketConnection = true -).build(); - -CometChat.init("APP_ID", appSettings, - onSuccess: (msg) => debugPrint("Init success"), - onError: (e) => debugPrint("Init failed: ${e.message}"), -); - -// 2. Login (check session first) -CometChat.getLoggedInUser( - onSuccess: (user) { - if (user == null) { - CometChat.login("cometchat-uid-1", "AUTH_KEY", // dev only — use Auth Token in production - onSuccess: (user) => debugPrint("Login success"), - onError: (e) => debugPrint("Login failed"), - ); - } - }, - onError: (e) => debugPrint("Error: ${e.message}"), -); -``` + +**Faster Integration with UI Kits** -**Credentials:** App ID, Region, Auth Key from [CometChat Dashboard](https://app.cometchat.com) -**Test UIDs:** `cometchat-uid-1` through `cometchat-uid-5` - +If you're using CometChat UI Kits, messaging features can be quickly integrated with pre-built components: +- Conversation lists, message composers, and thread views +- Typing indicators, read receipts, and reactions +- Group management and user profiles -The CometChat Flutter SDK lets you add real-time messaging, voice, and video calling to any Flutter application — iOS, Android, Web, or Desktop. +👉 [Flutter UI Kit Overview](/ui-kit/flutter/overview) -## Requirements +Use this Chat SDK directly only if you need custom UI or advanced control. + -| Requirement | Minimum Version | -|-------------|-----------------| -| Flutter SDK | 1.2 | -| Android API Level | 21 | -| iOS | 11 | + +**Upgrading from v4?** -Requires AndroidX compatibility. For iOS platform setup, see [Setup](/sdk/flutter/setup#ios-setup). +If you're migrating an existing app from CometChat SDK v4, check out the [Upgrading from v4](/sdk/flutter/v5/upgrading-from-v4-guide) guide for breaking changes, deprecated methods, and migration instructions. + -## Getting Started +Before integrating the Chat SDK, ensure you have a [CometChat Account](https://app.cometchat.com/signup) with your App ID, Region, and Auth Key. Flutter SDK `>=1.2` is required, with Android API Level 21+ and iOS 11+. Users must exist in CometChat to send or receive messages — see [Authentication](/sdk/flutter/v5/authentication-overview) for details. - - - [Sign up for CometChat](https://app.cometchat.com) and create an app. Note your App ID, Region, and Auth Key from the Dashboard. - - - Add the SDK to your project and initialize it with your credentials. See [Setup](/sdk/flutter/setup). - - - Log in users with Auth Key (development) or Auth Token (production). See [Authentication](/sdk/flutter/authentication-overview). - - - Send your first message, make a call, or manage users and groups. - - +```mermaid +sequenceDiagram + participant App + participant CometChat -## Features + App->>CometChat: init(appId, appSettings) + App->>CometChat: login(uid, authKey) + CometChat-->>App: User + App->>CometChat: sendMessage() / addMessageListener() + CometChat-->>App: Real-time events (messages, typing, presence) + App->>CometChat: logout() +``` - - 1:1 and group chat, threads, reactions, typing indicators, read receipts, file sharing, and interactive messages. - - - Ringing flows, direct call sessions, standalone calling, recording, and screen sharing. - - - Create, retrieve, and manage users. Track online/offline presence and block/unblock users. - - - Public, private, and password-protected groups with member management, roles, and ownership transfer. - - -## Sample Apps + + Send text, media, and custom messages in 1-on-1 or group conversations + -Explore working examples with full source code: + + Create, join, and manage group conversations with member roles and scopes + - - - Flutter sample app - - + + Listen for messages, typing indicators, read receipts, and presence changes in real time + -## UI Kits + + Show real-time typing status for users and groups + -Skip the UI work — use pre-built, customizable components: + + Track online/offline status of users with configurable subscription modes + - - - Flutter UI Kit - - + + Add and manage emoji reactions on messages + -## Resources + + Organize conversations with message threads + - - - UIDs, GUIDs, auth tokens, and core SDK concepts - - - Message categories, types, and hierarchy - - - Latest SDK version and release notes - - - Migration guide for V3 users - - - Common issues and solutions - - + + Track message delivery and read status + -## Next Steps + + Integrate AI-powered agents into your chat experience + + + + Voice and video calling with built-in UI components + - - - Install and initialize the CometChat Flutter SDK - - - Log in users with Auth Key or Auth Token - - - Send your first text, media, or custom message - - - UIDs, GUIDs, auth tokens, and core SDK concepts - + +| Component | Description | +|-----------|-------------| +| `CometChat` | Main entry point for initialization, authentication, messaging, and real-time events | +| `AppSettings` | Configuration for SDK initialization (App ID, Region, presence subscription) | +| `User` | Represents a CometChat user with UID, name, avatar, and metadata | +| `Group` | Represents a group conversation with GUID, type, and member management | +| `BaseMessage` | Base class for all message types (text, media, custom, action) | +| `MessageListener` | Event interface for real-time message, typing, and receipt events | +| `ConnectionListener` | Event interface for WebSocket connection status changes | diff --git a/sdk/flutter/reactions.mdx b/sdk/flutter/reactions.mdx index 7f0c3e057..86824cf6c 100644 --- a/sdk/flutter/reactions.mdx +++ b/sdk/flutter/reactions.mdx @@ -1,333 +1,79 @@ --- title: "Reactions" -sidebarTitle: "Reactions" -description: "Add, remove, and fetch emoji reactions on messages in your Flutter chat application using CometChat SDK." --- - -| Field | Value | -| --- | --- | -| Key Classes | `BaseMessage`, `Reaction`, `ReactionCount`, `ReactionEvent`, `ReactionsRequest`, `ReactionsRequestBuilder` | -| Key Methods | `CometChat.addReaction()`, `CometChat.removeReaction()`, `ReactionsRequest.fetchNext()`, `ReactionsRequest.fetchPrevious()`, `CometChatHelper.updateMessageWithReactionInfo()` | -| Listener Events | `onMessageReactionAdded`, `onMessageReactionRemoved` | -| Prerequisites | SDK initialized, user logged in | -```dart -// Add a reaction to a message -CometChat.addReaction(messageId, "\U0001f600", onSuccess: (message) { - debugPrint("Reaction added: ${message.getReactions().last}"); -}, onError: (e) {}); - -// Remove a reaction from a message -CometChat.removeReaction(messageId, "\U0001f600", onSuccess: (message) { - debugPrint("Reaction removed"); -}, onError: (e) {}); - -// Fetch reactions for a message -ReactionsRequest request = (ReactionsRequestBuilder() - ..messageId = messageId - ..limit = 30 -).build(); -request.fetchNext(onSuccess: (reactions) {}, onError: (e) {}); - -// Listen for reaction events -CometChat.addMessageListener("LISTENER_ID", MessageListener( - onMessageReactionAdded: (ReactionEvent event) {}, - onMessageReactionRemoved: (ReactionEvent event) {}, -)); -``` - - - -Reactions let users respond to messages with emoji. You can add or remove reactions, fetch all reactions on a message, listen for reaction events in real time, and update your UI when reactions change. - -Reactions work on text messages, media messages, and custom messages. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +Enhance user engagement in your chat application with message reactions. Users can express their emotions using reactions to messages. This feature allows users to add or remove reactions, and to fetch all reactions on a message. You can also listen to reaction events in real-time. Let's see how to work with reactions in CometChat's Flutter SDK. ## Add a Reaction - -Call `addReaction()` with a message ID and an emoji string. - -| Parameter | Type | Description | -| --- | --- | --- | -| `messageId` | `int` | The ID of the message to react to. | -| `reaction` | `String` | The reaction emoji (e.g., `"\U0001f600"`, `"\U0001f44d"`). | +Users can add a reaction to a message by calling `addReaction` with the message ID and the reaction emoji. ```dart int messageId = 1; -CometChat.addReaction(messageId, "\U0001f634", onSuccess: (message) { +CometChat.addReaction(messageId, "😴", onSuccess: (message) { debugPrint("Success : ${message.getReactions().last}"); }, onError: (e) { debugPrint("Error: ${e.message}"); }); ``` + - -**On Success** — A `BaseMessage` object representing the message with the reaction added: - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `401` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below \u2193](#add-reaction-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554750` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below \u2193](#add-reaction-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to process the reaction."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - + +You can react on Text, Media and Custom messages. + ## Remove a Reaction - -Call `removeReaction()` with the same message ID and emoji. - -| Parameter | Type | Description | -| --- | --- | --- | -| `messageId` | `int` | The ID of the message to remove the reaction from. | -| `reaction` | `String` | The reaction emoji to remove (e.g., `"\U0001f600"`, `"\U0001f44d"`). | +Removing a reaction from a message can be done using the `removeReaction` method. ```dart + int messageId = 1; -CometChat.removeReaction(messageId, "\U0001f634", onSuccess: (message) { +CometChat.removeReaction(messageId, "😴", onSuccess: (message) { debugPrint("Success : ${message.getReactions().last}"); }, onError: (e) { debugPrint("Error: ${e.message}"); }); ``` - - - - -**On Success** — A `BaseMessage` object representing the message with the reaction removed: - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `401` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below \u2193](#remove-reaction-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554750` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below \u2193](#remove-reaction-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to process the reaction."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - -Both `addReaction()` and `removeReaction()` return a [`BaseMessage`](/sdk/reference/messages#basemessage) object with the updated reactions. - -## Read Reaction Data from a Message - -Any `BaseMessage` exposes reaction data through its `reactions` property: - -### Get All Reactions - -Use the `reactions` property to retrieve the list of reactions on a message. Returns a `List<`[`ReactionCount`](/sdk/reference/auxiliary#reactioncount)`>` containing the reactions, or an empty list if no one has reacted. - - - -```dart -message.reactions -``` -### Check if the Logged-in User Has Reacted -Use the `reactedByMe` property on any `ReactionCount` object to check whether the logged-in user has reacted with that particular emoji. Returns `true` if they have, `false` otherwise. +## Fetch Reactions for a Message +To get all reactions for a specific message, first create a `ReactionRequest` using `ReactionRequestBuilder`. You can specify the number of reactions to fetch with `limit` with max limit 100. For this, you will require the ID of the message. This ID needs to be passed to the `messageId` property of the builder class. The `reaction` property will allow you to fetch details for specific reaction or emoji. - - -```dart -for (ReactionCount reactionCount in message.reactions) { - debugPrint("isReactedByMe ${reactionCount.reactedByMe}"); //Return true is logged-in user reacted on that message, otherwise false -} -``` - - -## Fetch Reactions for a Message +| Setting | Description | +|-----------------------------------|------------------------------------------------------------------------------------------------------------------| +| `setMessageId(int value)` | Specifies the unique identifier of the message for which you want to fetch reactions. This parameter is mandatory as it tells the SDK which message's reactions are being requested. | +| `setReaction(String value)` | Filters the reactions fetched by the specified reaction type (e.g., "😊", "😂", "👍"). When set, this method will cause the ReactionRequest to only retrieve details of the provided reaction for the given message. | -To get the full list of who reacted with what on a specific message, use `ReactionsRequestBuilder`. You can paginate through results with `fetchNext()` and `fetchPrevious()` (max 100 per request). -| Builder Property | Type | Description | -| --- | --- | --- | -| `messageId` | `int?` | The message ID to fetch reactions for. Required. | -| `reaction` | `String?` | Filter to a specific emoji (e.g., `"\U0001f60a"`). When set, only reactions matching this emoji are returned. | -| `limit` | `int` | Number of reactions to fetch per request. Default is `10`. | +## Fetch Next +The fetchNext() method fetches the next set of reactions for the message. -### Fetch Next -The `fetchNext()` method fetches the next set of reactions for the message. ```dart int messageId = 1; -ReactionsRequest reactionsRequest = (ReactionsRequestBuilder() - ..limit = 30 - ..messageId = messageId -).build(); +ReactionRequest reactionRequest = (ReactionRequestBuilder()..limit = 30..messageId = messageId).build(); -reactionsRequest.fetchNext( - onSuccess: (reactions) { - for (Reaction reaction in reactions) { - debugPrint("Success: $reaction"); +reactionRequest.fetchNext( + onSuccess: (messageReactions) { + for (MessageReaction messageReaction in messageReactions) { + debugPrint("Success: ${messageReaction.getReactions()}"); } }, onError: (e) { @@ -335,62 +81,11 @@ reactionsRequest.fetchNext( }, ); ``` + -The `fetchNext()` method returns a `List` representing individual user reactions on the message. - - -**On Success** — A list of `Reaction` objects for the message: - - - -**Reaction Object (per item in list):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | string | Unique reaction identifier | `"r1"` | -| `messageId` | number | ID of the message this reaction belongs to | `1` | -| `reaction` | string | The reaction emoji | `"\U0001f634"` | -| `uid` | string | UID of the user who reacted | `"cometchat-uid-1"` | -| `reactedAt` | number | Epoch timestamp when the reaction was added | `1745554729` | -| `reactedBy` | object | User who added the reaction | [See below \u2193](#fetch-next-reaction-reactedby-object) | - ---- - - - -**`reactedBy` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - -### Fetch Previous - +## Fetch Previous The `fetchPrevious()` method fetches the previous set of reactions for the message. @@ -398,15 +93,12 @@ The `fetchPrevious()` method fetches the previous set of reactions for the messa ```dart int messageId = 1; -ReactionsRequest reactionsRequest = (ReactionsRequestBuilder() - ..limit = 30 - ..messageId = messageId -).build(); +ReactionRequest reactionRequest = (ReactionRequestBuilder()..limit = 30..messageId = messageId).build(); -reactionsRequest.fetchPrevious( - onSuccess: (reactions) { - for (Reaction reaction in reactions) { - debugPrint("Success: $reaction"); +reactionRequest.fetchPrevious( + onSuccess: (messageReactions) { + for (MessageReaction messageReaction in messageReactions) { + debugPrint("Success: ${messageReaction.getReactions()}"); } }, onError: (e) { @@ -414,61 +106,13 @@ reactionsRequest.fetchPrevious( }, ); ``` + - -**On Success** — A list of `Reaction` objects for the message: - - - -**Reaction Object (per item in list):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | string | Unique reaction identifier | `"r1"` | -| `messageId` | number | ID of the message this reaction belongs to | `1` | -| `reaction` | string | The reaction emoji | `"\U0001f634"` | -| `uid` | string | UID of the user who reacted | `"cometchat-uid-1"` | -| `reactedAt` | number | Epoch timestamp when the reaction was added | `1745554729` | -| `reactedBy` | object | User who added the reaction | [See below \u2193](#fetch-previous-reaction-reactedby-object) | - ---- - - - -**`reactedBy` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - - - - +## Real-time Reaction Events +Keep the chat interactive with real-time updates for reactions. Register a listener for these events and make your UI responsive. -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - -## Real-Time Reaction Events - -Register a `MessageListener` to receive reaction events as they happen. This is how you keep your UI in sync when other users add or remove reactions. @@ -492,85 +136,78 @@ class MyClass with MessageListener { } } ``` + -Each reaction listener callback receives a `ReactionEvent` object containing: -| Parameter | Type | Description | -| --- | --- | --- | -| `reaction` | `Reaction?` | The reaction that was added or removed. | -| `receiverId` | `String?` | The UID or GUID of the receiver. | -| `receiverType` | `String?` | The type of the receiver (`"user"` or `"group"`). | -| `conversationId` | `String?` | The unique conversation identifier. | -| `parentMessageId` | `int?` | The ID of the parent message (for threaded messages). | -### Remove the Listener +## Removing a Reaction Listener +To stop listening for reaction events, remove the listener as follows: ```dart String listenerID = "UNIQUE_LISTENER_ID"; -CometChat.removeMessageListener(listenerID); +CometChat.removeMessageReactionListener(listenerID); ``` + - -Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. - +## Get Reactions List +To retrieve the list of reactions reacted on particular message, you can use the `reactions` property on `TextMessage`, `MediaMessage` and `CustomMessage`. This property will return a List of `ReactionCount` containing the reactions, or an empty list if no one reacted on the message. + + + +```dart +message.reactions +``` -## Update a Message with Reaction Info + + -When you receive a real-time reaction event, you'll want to update the corresponding message object so your UI reflects the change. `CometChatHelper.updateMessageWithReactionInfo()` does this — it takes the `BaseMessage` instance, the `Reaction` event data, and the action type, then returns the updated message. -| Parameter | Type | Description | -|-----------|------|-------------| -| `baseMessage` | `BaseMessage` | The message object to update. | -| `messageReaction` | `Reaction` | The reaction event received from the listener. | -| `action` | `String` | `ReactionAction.reactionAdded` or `ReactionAction.reactionRemoved` | +## Check if Logged-in User has Reacted on Message +To check if the logged-in user has reacted on a particular message or not, You can use the `reactedByMe` property on any `ReactionCount` object. This method will return a boolean value, `true` if the logged-in user has reacted on that message, otherwise `false`. ```dart -// Pass the message and Reaction from your listener (e.g. onMessageReactionAdded) -Future refreshMessageWithReaction( - BaseMessage message, - Reaction messageReaction, -) async { - // The received reaction event action type: reactionAdded or reactionRemoved - var action = ReactionAction.reactionAdded; - - BaseMessage? modifiedBaseMessage = - await CometChatHelper.updateMessageWithReactionInfo( - message, - messageReaction, - action, - ); - // Use modifiedBaseMessage?.reactions to refresh your UI +for (ReactionCount reactionCount in message.reactions) { + debugPrint("isReactedByMe ${reactionCount.reactedByMe}"); //Return true is logged-in user reacted on that message, otherwise false } ``` + -After calling this method, use `modifiedBaseMessage?.reactions` to get the latest reactions and refresh your UI. +## Updated Message With Reaction Info +When a user adds or removes a reaction, you will receive a real-time event. Once you receive the real time event you would want to update the message with the latest reaction information. To do so you can use the `updateMessageWithReactionInfo()` method. ---- +The `updateMessageWithReactionInfo()` method provides a seamless way to update the reactions on a message instance (`BaseMessage`) in real-time. This method ensures that when a reaction is added or removed from a message, the BaseMessage object's `getReactions()` property reflects this change immediately. + +When you receive a real-time reaction event (MessageReaction), call the `updateMessageWithReactionInfo()` method, passing the BaseMessage instance (message), event data (MessageReaction) and reaction event action type (`ReactionAction.REACTION_ADDED` or `ReactionAction.REACTION_REMOVED`) that corresponds to the message being reacted to. + + + +```dart +// The message to which the reaction is related +BaseMessage message = ...; + +// The reaction event data received in real-time +MessageReaction messageReaction = ...; -## Next Steps - - - - Send text, media, and custom messages to users and groups - - - Listen for incoming messages in real-time and fetch missed messages - - - Create and manage message threads - - - Mention users and groups in messages - - +// The recieved reaction event real-time action type. Can be CometChatConstants.REACTION_ADDED or CometChatConstants.REACTION_REMOVED +var action = CometChatConstants.REACTION_ADDED; + +BaseMessage modifiedBaseMessage = await CometChatHelper.updateMessageWithReactionInfo( + baseMessage, + messageReaction, + action +); +``` + + + diff --git a/sdk/flutter/real-time-listeners.mdx b/sdk/flutter/real-time-listeners.mdx index 0ba1a4508..e1685bb8e 100644 --- a/sdk/flutter/real-time-listeners.mdx +++ b/sdk/flutter/real-time-listeners.mdx @@ -1,235 +1,26 @@ --- title: "All Real Time Listeners" -sidebarTitle: "Real-Time Listeners" -description: "Learn how to register and manage real-time event listeners for messages, users, groups, calls, connections, login, and AI in the CometChat Flutter SDK." --- - -```dart -// Message Listener - receive messages in real-time -CometChat.addMessageListener("message_listener", MessageListener( - onTextMessageReceived: (TextMessage message) { - debugPrint("Text received: ${message.text}"); - }, - onMediaMessageReceived: (MediaMessage message) { - debugPrint("Media received: ${message.attachment?.fileUrl}"); - }, -)); - -// User Listener - track user online/offline status -CometChat.addUserListener("user_listener", UserListener( - onUserOnline: (User user) { - debugPrint("${user.name} is online"); - }, - onUserOffline: (User user) { - debugPrint("${user.name} is offline"); - }, -)); - -// Group Listener - track group membership changes -CometChat.addGroupListener("group_listener", GroupListener( - onGroupMemberJoined: (action, joinedUser, joinedGroup) { - debugPrint("${joinedUser.name} joined ${joinedGroup.name}"); - }, - onGroupMemberLeft: (action, leftUser, leftGroup) { - debugPrint("${leftUser.name} left ${leftGroup.name}"); - }, -)); - -// Call Listener - track incoming/outgoing calls -CometChat.addCallListener("call_listener", CallListener( - onIncomingCallReceived: (Call call) { - debugPrint("Incoming call from: ${call.sender?.name}"); - }, - onOutgoingCallAccepted: (Call call) { - debugPrint("Call accepted"); - }, -)); - -// Connection Listener - monitor WebSocket connection -CometChat.addConnectionListener("conn_listener", ConnectionListener( - onConnected: () => debugPrint("Connected"), - onDisconnected: () => debugPrint("Disconnected"), -)); - -// Remove listeners when no longer needed -CometChat.removeMessageListener("message_listener"); -CometChat.removeUserListener("user_listener"); -CometChat.removeGroupListener("group_listener"); -CometChat.removeCallListener("call_listener"); -CometChat.removeConnectionListener("conn_listener"); -``` - - -CometChat provides real-time event listeners to keep your app updated with live changes. These listeners notify your app when messages are received, users come online/offline, group membership changes occur, calls are initiated, and connection state changes. - - -**Listener Cleanup Required:** Always remove listeners when they're no longer needed (e.g., on widget dispose or page navigation). Failing to remove listeners can cause memory leaks and duplicate event handling. - - -CometChat provides 7 listener types: - -1. [MessageListener](#message-listener) -2. [UserListener](#user-listener) -3. [GroupListener](#group-listener) -4. [CallListener](#call-listener) -5. [ConnectionListener](#connection-listener) -6. [LoginListener](#login-listener) -7. [AIAssistantListener](#ai-assistant-listener) - -## Message Listener - -The `MessageListener` class provides you with live events related to messages. Below are the callback methods provided by the `MessageListener` class. - -### MessageListener Events - -| Method | Parameter Type | Description | -| ------ | -------------- | ----------- | -| `onTextMessageReceived(TextMessage message)` | [`TextMessage`](/sdk/reference/messages#textmessage) | Triggered when a text message is received | -| `onMediaMessageReceived(MediaMessage message)` | [`MediaMessage`](/sdk/reference/messages#mediamessage) | Triggered when a media message is received | -| `onCustomMessageReceived(CustomMessage message)` | [`CustomMessage`](/sdk/reference/messages#custommessage) | Triggered when a custom message is received | -| `onTypingStarted(TypingIndicator typingIndicator)` | [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) | Triggered when a user starts typing in a user/group conversation | -| `onTypingEnded(TypingIndicator typingIndicator)` | [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) | Triggered when a user stops typing in a user/group conversation | -| `onMessagesDelivered(MessageReceipt messageReceipt)` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are marked as delivered for a conversation | -| `onMessagesRead(MessageReceipt messageReceipt)` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are marked as read for a conversation | -| `onMessagesDeliveredToAll(MessageReceipt messageReceipt)` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are delivered to all participants | -| `onMessagesReadByAll(MessageReceipt messageReceipt)` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are read by all participants | -| `onMessageEdited(BaseMessage message)` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is edited in a user/group conversation | -| `onMessageDeleted(BaseMessage message)` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is deleted in a user/group conversation | -| `onMessageModerated(BaseMessage message)` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is moderated | -| `onTransientMessageReceived(TransientMessage message)` | [`TransientMessage`](/sdk/reference/auxiliary#transientmessage) | Triggered when a transient message is received | -| `onInteractiveMessageReceived(InteractiveMessage message)` | `InteractiveMessage` | Triggered when an interactive message is received | -| `onInteractionGoalCompleted(InteractionReceipt receipt)` | `InteractionReceipt` | Triggered when an interaction goal is achieved | -| `onMessageReactionAdded(ReactionEvent reactionEvent)` | `ReactionEvent` | Triggered when a reaction is added to a message | -| `onMessageReactionRemoved(ReactionEvent reactionEvent)` | `ReactionEvent` | Triggered when a reaction is removed from a message | -| `onAIAssistantMessageReceived(AIAssistantMessage message)` | `AIAssistantMessage` | Triggered when an AI assistant message is received | -| `onAIToolResultReceived(AIToolResultMessage message)` | `AIToolResultMessage` | Triggered when an AI tool result message is received | -| `onAIToolArgumentsReceived(AIToolArgumentMessage message)` | `AIToolArgumentMessage` | Triggered when AI tool arguments are received | - -To add the `MessageListener`, you need to use the `addMessageListener()` method provided by the `CometChat` class. - -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | -| `listener` | `MessageListener` | An instance of a class that implements the `MessageListener` mixin | - - - -```dart -class Class_Name with MessageListener { - - //CometChat.addMessageListener("listenerId", this); - @override - void onTextMessageReceived(TextMessage textMessage) { - - } - - @override - void onMediaMessageReceived(MediaMessage mediaMessage) { - - } - - @override - void onCustomMessageReceived(CustomMessage customMessage) { - - } - - @override - void onTypingStarted(TypingIndicator typingIndicator) { - - } - - @override - void onTypingEnded(TypingIndicator typingIndicator) { - - } - - - @override - void onMessagesDelivered(MessageReceipt messageReceipt) { - - } - - @override - void onMessagesRead(MessageReceipt messageReceipt) { - - } - - @override - void onMessageEdited(BaseMessage message) { - - } - - @override - void onMessageDeleted(BaseMessage message) { - - } - - @override - void onInteractionGoalCompleted(InteractionReceipt receipt) { - - - } - - @override - void onInteractiveMessageReceived(InteractiveMessage message) { - - } - - - @Override - public void onTransientMessageReceived(TransientMessage transientMessage) { - - } - - @Override - public void onMessageReactionAdded(ReactionEvent reactionEvent) { - - } - - @Override - public void onMessageReactionRemoved(ReactionEvent reactionEvent) { - - } -} -``` - - - - - -where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. +CometChat provides 4 listeners viz. -Once the activity/fragment where the `MessageListener` is declared is not in use, you need to remove the listener using the `removeMessageListener()` method which takes the id of the listener to be removed as the parameter. We suggest you call this method in the `onPause()` method of the activity/fragment. - - - -```dart -CometChat.removeMessageListener(UNIQUE_LISTENER_ID) -``` - - +1. [UserListener](#user-listener) +2. [GroupListener](#group-listener) +3. [MessageListener](#message-listener) ## User Listener The `UserListener` class provides you with live events related to users. Below are the callback methods provided by the `UserListener` class. -### UserListener Events - -| Method | Parameter Type | Description | -| ------ | -------------- | ----------- | -| `onUserOnline(User user)` | [`User`](/sdk/reference/entities#user) | Triggered when a user comes online and is available to chat. The details of the user can be obtained from the `User` object received as the method parameter. | -| `onUserOffline(User user)` | [`User`](/sdk/reference/entities#user) | Triggered when a user goes offline. The details of the user can be obtained from the `User` object received as the parameter. | +| Method | Information | +| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `onUserOnline(User user)` | This method is triggered when a user comes online and is available to chat. The details of the user can be obtained from the user object received as the method parameter. | +| `onUserOffline(User user)` | This method is triggered when a user goes offline. The details of the user can be obtained from the User object received as the parameter. | To add the `UserListener`, you need to use the `addUserListener()` method provided by the `CometChat` class. -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | -| `listener` | `UserListener` | An instance of a class that implements the `UserListener` mixin | - ```dart @@ -270,25 +61,18 @@ CometChat.removeUserListener(UNIQUE_LISTENER_ID) The `GroupListener` class provides you with all the real-time events related to groups. Below are the callback methods provided by the `GroupListener` class. -### GroupListener Events - -| Method | Parameter Types | Description | -| ------ | --------------- | ----------- | -| `onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user joins any group. All the members present in the group will receive this event. | -| `onGroupMemberLeft(Action action, User leftUser, Group leftGroup)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user who was a member of any group leaves the group. All the members of the group receive this event. | -| `onGroupMemberKicked(Action action, User kickedUser, User kickedBy, Group kickedFrom)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user is kicked from a group. All the members including the user receive this event. | -| `onGroupMemberBanned(Action action, User bannedUser, User bannedBy, Group bannedFrom)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user is banned from a group. All the members including the user receive this event. | -| `onGroupMemberUnbanned(Action action, User unbannedUser, User unbannedBy, Group unbannedFrom)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user is unbanned from a group. All the members of the group receive this event. | -| `onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), `String`, `String`, [`Group`](/sdk/reference/entities#group) | Triggered when the scope of any group member has been changed. All the members that are a part of that group receive this event. | -| `onMemberAddedToGroup(Action action, User addedBy, User userAdded, Group addedTo)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user is added to any group. All the members including the user himself receive this event. | +| Method | Information | +| ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| `onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup)` | This method is triggered when a user joins any group. All the members present in the group will receive this event. | +| `onGroupMemberLeft(Action action, User leftUser, Group leftGroup)` | This method is triggered when a user who was a member of any group leaves the group. All the members of the group receive this event. | +| `onGroupMemberKicked(Action action, User kickedUser, User kickedBy, Group kickedFrom)` | This method is triggered when a user is kicked from a group. All the members including the user receive this event | +| `onGroupMemberBanned(Action action, User bannedUser, User bannedBy, Group bannedFrom)` | This method is triggered when a user is banned from a group. All the members including the user receive this event | +| `onGroupMemberUnbanned(Action action, User unbannedUser, User unbannedBy, Group unbannedFrom)` | This method is triggered when a user is banned from a group. All the members of the group receive this event. | +| `onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group)` | This method is triggered when the scope of any Group Member has been changed. All the members that are a part of that group receive this event | +| `onMemberAddedToGroup(Action action, User addedBy, User userAdded, Group addedTo)` | This method is triggered when a user is added to any group. All the members including the user himself receive this event. | To add the `GroupListener`, you need to use the `addGroupListener()` method provided by the `CometChat` class. -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | -| `listener` | `GroupListener` | An instance of a class that implements the `GroupListener` mixin | - ```dart @@ -351,244 +135,105 @@ CometChat.removeGroupListener(UNIQUE_LISTENER_ID) -## Call Listener - -The `CallListener` class provides you with real-time events related to calls. Below are the callback methods provided by the `CallListener` class. - -### CallListener Events +## Message Listener -| Method | Parameter Type | Description | -| ------ | -------------- | ----------- | -| `onIncomingCallReceived(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when an incoming call is received | -| `onOutgoingCallAccepted(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when an outgoing call is accepted by the receiver | -| `onOutgoingCallRejected(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when an outgoing call is rejected by the receiver | -| `onIncomingCallCancelled(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when an incoming call is cancelled by the caller | -| `onCallEndedMessageReceived(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when a call ended message is received | +The `MessageListener` class provides you with live events related to messages. Below are the callback methods provided by the `MessageListener` class. -To add the `CallListener`, you need to use the `addCallListener()` method provided by the `CometChat` class. +| Method | Information | +| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `onTextMessageReceived(TextMessage message)` | This event is triggered when a Text Message is received. | +| `onMediaMessageReceived(MediaMessage message)` | This event is triggered when a Media Message is received. | +| `onCustomMessageReceived(CustomMessage message)` | This event is triggered when a Custom Message is received. | +| `onTypingStarted(TypingIndicator typingIndicator)` | This event is triggered when a user starts typing in a user/group conversation | +| `onTypingEnded(TypingIndicator typingIndicator)` | This event is triggered when a user stops typing in a user/group conversation. | +| `onMessagesDelivered(MessageReceipt messageReceipt)` | This event is triggered when a set of messages are marked as delivered for any particular conversation. | +| `onMessagesRead(MessageReceipt messageReceipt)` | This event is triggered when a set of messages are marked as read for any particular conversation. | +| `onMessageEdited(BaseMessage message)` | This method is triggered when a particular message has been edited in a user/group conversation. | +| `onMessageDeleted(BaseMessage message)` | This event is triggered when a particular message is deleted in a user/group conversation. | +| `onInteractiveMessageReceived(InteractiveMessage message)` | This event is triggered when an Interactive Message is received. | +| `onInteractionGoalCompleted(InteractionReceipt receipt)` | This event is triggered when an interaction Goal is achieved. | +| `onTransientMessageReceived(TransientMessage transientMessage)` | This event is triggered when a Transient Message is received. | +| `onMessageReactionAdded(ReactionEvent reactionEvent)` | This event is triggered when a reaction is added to a message in a user/group conversation. | +| `onMessageReactionRemoved(ReactionEvent reactionEvent)` | This event is triggered when a reaction is remove from a message in a user/group conversation. | -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | -| `listener` | `CallListener` | An instance of a class that implements the `CallListener` mixin | +To add the `MessageListener`, you need to use the `addMessageListener()` method provided by the `CometChat` class. ```dart -class Class_Name with CallListener { - //CometChat.addCallListener("UNIQUE_LISTENER_ID", this); +class Class_Name with MessageListener { - @override - void onIncomingCallReceived(Call call) { + //CometChat.addMessageListener("listenerId", this); + @override + void onTextMessageReceived(TextMessage textMessage) { } @override - void onOutgoingCallAccepted(Call call) { + void onMediaMessageReceived(MediaMessage mediaMessage) { } @override - void onOutgoingCallRejected(Call call) { + void onCustomMessageReceived(CustomMessage customMessage) { } - @override - void onIncomingCallCancelled(Call call) { + @override + void onTypingStarted(TypingIndicator typingIndicator) { } @override - void onCallEndedMessageReceived(Call call) { + void onTypingEnded(TypingIndicator typingIndicator) { } -} -``` - - - - - -where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. - -Once the `CallListener` is no longer needed, remove it using the `removeCallListener()` method. - - - -```dart -CometChat.removeCallListener(UNIQUE_LISTENER_ID) -``` - - - - - -## Connection Listener - -The `ConnectionListener` class provides you with real-time events related to the WebSocket connection status. Below are the callback methods provided by the `ConnectionListener` class. - -### ConnectionListener Events - -| Method | Parameter Type | Description | -| ------ | -------------- | ----------- | -| `onConnected()` | — | Triggered when the SDK successfully establishes a connection to the WebSocket server | -| `onConnecting()` | — | Triggered when the SDK is attempting to establish a connection to the WebSocket server | -| `onDisconnected()` | — | Triggered when the SDK gets disconnected due to network fluctuations or other issues | -| `onFeatureThrottled()` | — | Triggered when CometChat automatically toggles off certain features to prevent performance loss | -| `onConnectionError(CometChatException error)` | `CometChatException` | Triggered when an error occurs while maintaining the WebSocket connection | - -To add the `ConnectionListener`, you need to use the `addConnectionListener()` method provided by the `CometChat` class. - -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | -| `listener` | `ConnectionListener` | An instance of a class that implements the `ConnectionListener` mixin | - - - -```dart -class Class_Name with ConnectionListener { - //CometChat.addConnectionListener("UNIQUE_LISTENER_ID", this); @override - void onConnected() { + void onMessagesDelivered(MessageReceipt messageReceipt) { } @override - void onConnecting() { + void onMessagesRead(MessageReceipt messageReceipt) { } @override - void onDisconnected() { + void onMessageEdited(BaseMessage message) { } @override - void onFeatureThrottled() { + void onMessageDeleted(BaseMessage message) { } @override - void onConnectionError(CometChatException error) { - - } - -} -``` - - - - - -where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. - -Once the `ConnectionListener` is no longer needed, remove it using the `removeConnectionListener()` method. - - - -```dart -CometChat.removeConnectionListener(UNIQUE_LISTENER_ID) -``` - - - - - -## Login Listener - -The `LoginListener` class provides you with real-time events related to user authentication. Below are the callback methods provided by the `LoginListener` class. - -### LoginListener Events - -| Method | Parameter Type | Description | -| ------ | -------------- | ----------- | -| `loginSuccess(User user)` | [`User`](/sdk/reference/entities#user) | Triggered when a user successfully logs in | -| `loginFailure(CometChatException e)` | `CometChatException` | Triggered when a login attempt fails | -| `logoutSuccess()` | — | Triggered when a user successfully logs out | -| `logoutFailure(CometChatException e)` | `CometChatException` | Triggered when a logout attempt fails | - -To add the `LoginListener`, you need to use the `addLoginListener()` method provided by the `CometChat` class. - -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | -| `listener` | `LoginListener` | An instance of a class that implements the `LoginListener` mixin | - - - -```dart -class Class_Name with LoginListener { - //CometChat.addLoginListener("UNIQUE_LISTENER_ID", this); + void onInteractionGoalCompleted(InteractionReceipt receipt) { - @override - void loginSuccess(User user) { } @override - void loginFailure(CometChatException e) { + void onInteractiveMessageReceived(InteractiveMessage message) { } - @override - void logoutSuccess() { - - } - @override - void logoutFailure(CometChatException e) { + @Override + public void onTransientMessageReceived(TransientMessage transientMessage) { } -} -``` - - - - - -where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. - -Once the `LoginListener` is no longer needed, remove it using the `removeLoginListener()` method. - - - -```dart -CometChat.removeLoginListener(UNIQUE_LISTENER_ID) -``` - - - - - -## AI Assistant Listener - -The `AIAssistantListener` class provides you with real-time events related to AI assistant interactions. Below are the callback methods provided by the `AIAssistantListener` class. - -### AIAssistantListener Events - -| Method | Parameter Type | Description | -| ------ | -------------- | ----------- | -| `onAIAssistantEventReceived(AIAssistantBaseEvent aiAssistantBaseEvent)` | [`AIAssistantBaseEvent`](/sdk/reference/messages#aiassistantbaseevent) | Triggered when an AI assistant event is received | - -To add the `AIAssistantListener`, you need to use the `addAIAssistantListener()` method provided by the `CometChat` class. - -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | -| `listener` | `AIAssistantListener` | An instance of a class that implements the `AIAssistantListener` mixin | + @Override + public void onMessageReactionAdded(ReactionEvent reactionEvent) { - - -```dart -class Class_Name with AIAssistantListener { - //CometChat.addAIAssistantListener("UNIQUE_LISTENER_ID", this); + } - @override - void onAIAssistantEventReceived(AIAssistantBaseEvent aiAssistantBaseEvent) { + @Override + public void onMessageReactionRemoved(ReactionEvent reactionEvent) { } @@ -601,39 +246,4 @@ class Class_Name with AIAssistantListener { where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. -Once the `AIAssistantListener` is no longer needed, remove it using the `removeAIAssistantListener()` method. - - - -```dart -CometChat.removeAIAssistantListener(UNIQUE_LISTENER_ID) -``` - - - - - ---- - -## Next Steps - - - - Handle incoming messages in real-time using message listeners - - - Track when users come online or go offline - - - Show real-time typing status in conversations - - - Track message delivery and read status - - - Monitor SDK connection to CometChat servers - - - Monitor user login and logout events - - +Once the activity/fragment where the `MessageListener` is declared is not in use, you need to remove the listener using the `removeMessageListener()` method which takes the id of the listener to be removed as the parameter. We suggest you call this method in the `onPause()` method of the activity/fragment. diff --git a/sdk/flutter/receive-messages.mdx b/sdk/flutter/receive-messages.mdx index f4f376439..92246df7f 100644 --- a/sdk/flutter/receive-messages.mdx +++ b/sdk/flutter/receive-messages.mdx @@ -1,29 +1,14 @@ --- title: "Receive A Message" -sidebarTitle: "Receive Messages" -description: "Learn how to receive real-time messages and fetch missed or historical messages using the CometChat Flutter SDK." --- - -| Field | Value | -| --- | --- | -| Key Classes | `MessageListener`, `MessagesRequest`, `MessagesRequestBuilder` | -| Key Methods | `addMessageListener()`, `removeMessageListener()`, `fetchPrevious()`, `fetchNext()`, `getLastDeliveredMessageId()`, `getUnreadMessageCount()` | -| Listener Events | `onTextMessageReceived`, `onMediaMessageReceived`, `onCustomMessageReceived`, `onTypingStarted`, `onTypingEnded`, `onMessagesDelivered`, `onMessagesRead`, `onMessageEdited`, `onMessageDeleted`, `onInteractiveMessageReceived`, `onMessageReactionAdded`, `onMessageReactionRemoved` | -| Prerequisites | SDK initialized, user logged in | - - Receiving messages with CometChat has two parts: 1. Adding a listener to receive [Real-time Messages](/sdk/flutter/receive-messages#real-time-messages) when your app is running 2. Calling a method to retrieve [Missed Messages](/sdk/flutter/receive-messages#missed-messages) when your app was not running - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - - ## Real-time Messages *In other words, as a recipient, how do I receive messages when my app is running?* @@ -70,39 +55,8 @@ onInteractiveMessageReceived(InteractiveMessage message) { | ---------- | ---------------------------------------------------------------------------------------------- | | listenerID | An ID that uniquely identifies that listener. We recommend using the activity or fragment name | -### MessageListener Events - -The `MessageListener` mixin provides the following event callbacks. All events are verified against the Flutter SDK source. - -| Event | Parameter Type | Description | -| --- | --- | --- | -| `onTextMessageReceived` | [`TextMessage`](/sdk/reference/messages#textmessage) | Triggered when a text message is received | -| `onMediaMessageReceived` | [`MediaMessage`](/sdk/reference/messages#mediamessage) | Triggered when a media message (image, video, audio, file) is received | -| `onCustomMessageReceived` | [`CustomMessage`](/sdk/reference/messages#custommessage) | Triggered when a custom message is received | -| `onTypingStarted` | [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) | Triggered when a user starts typing | -| `onTypingEnded` | [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) | Triggered when a user stops typing | -| `onMessagesDelivered` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are delivered to the recipient | -| `onMessagesRead` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are read by the recipient | -| `onMessagesDeliveredToAll` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are delivered to all group members | -| `onMessagesReadByAll` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are read by all group members | -| `onMessageEdited` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is edited | -| `onMessageDeleted` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is deleted | -| `onTransientMessageReceived` | [`TransientMessage`](/sdk/reference/auxiliary#transientmessage) | Triggered when a transient (non-persistent) message is received | -| `onInteractiveMessageReceived` | `InteractiveMessage` | Triggered when an interactive message is received | -| `onInteractionGoalCompleted` | `InteractionReceipt` | Triggered when an interaction goal is completed | -| `onMessageReactionAdded` | `ReactionEvent` | Triggered when a reaction is added to a message | -| `onMessageReactionRemoved` | `ReactionEvent` | Triggered when a reaction is removed from a message | -| `onMessageModerated` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is moderated | -| `onAIAssistantMessageReceived` | `AIAssistantMessage` | Triggered when an AI assistant message is received | -| `onAIToolResultReceived` | `AIToolResultMessage` | Triggered when an AI tool result is received | -| `onAIToolArgumentsReceived` | `AIToolArgumentMessage` | Triggered when AI tool arguments are received | - We recommend you remove the listener once the activity or fragment is not in use. Typically, this can be added in the `dispose()` method. - -Always remove message listeners when they're no longer needed (e.g., in the `dispose()` method of your StatefulWidget). Failing to remove listeners can cause memory leaks and duplicate event handling. - - ```dart @@ -165,94 +119,6 @@ messageRequest.fetchNext(onSuccess: (List list) { - -**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `501` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-missed-user-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-missed-user-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ### For a Particular Group Conversation @@ -285,94 +151,6 @@ messageRequest.fetchNext(onSuccess: (List list) { - -**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `502` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver group object | [See below ↓](#fetch-missed-group-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"group_cometchat-uid-1"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | GUID of the receiver group | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-missed-group-sender-object) | -| `receiverType` | string | Type of the receiver | `"group"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ## Unread Messages *In other words, as a logged-in user, how do I fetch the messages I've not read?* @@ -412,94 +190,6 @@ messageRequest.fetchPrevious(onSuccess: (List list) { - -**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `503` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-unread-user-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-unread-user-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ### For a Particular Group Conversation @@ -533,98 +223,10 @@ messageRequest.fetchPrevious(onSuccess: (List list) { - -**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `504` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-unread-group-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"group_cometchat-uid-1"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | GUID of the receiver group | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-unread-group-sender-object) | -| `receiverType` | string | Type of the receiver | `"group"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - Base Message -The list of messages received is in the form of objects of [`BaseMessage`](/sdk/reference/messages#basemessage) class. A `BaseMessage` can either be an object of the [`TextMessage`](/sdk/reference/messages#textmessage), [`MediaMessage`](/sdk/reference/messages#mediamessage), [`CustomMessage`](/sdk/reference/messages#custommessage), [`Action`](/sdk/reference/messages#action) or [`Call`](/sdk/reference/messages#call) class. You can use the `is` operator to check the type of object. +The list of messages received is in the form of objects of `BaseMessage` class. A `BaseMessage` can either be an object of the `TextMessage`, `MediaMessage`, `CustomMessage`, `Action` or `Call` class. You can use the `is` operator to check the type of object. @@ -665,94 +267,6 @@ messageRequest.fetchPrevious(onSuccess: (List list) { - -**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `505` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-history-user-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-history-user-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - Calling the `fetchPrevious()` method on the same object repeatedly allows you to fetch all the previous messages in a paginated way. ### For a Particular Group Conversation @@ -787,94 +301,6 @@ messageRequest.fetchPrevious(onSuccess: (List list) { - -**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `506` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-history-group-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"group_cometchat-uid-1"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | GUID of the receiver group | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-history-group-sender-object) | -| `receiverType` | string | Type of the receiver | `"group"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - Calling the `fetchPrevious()` method on the same object repeatedly allows you to fetch the entire conversation between the logged in user and the specified user. This can be implemented with upward scrolling to display the entire conversation. ## Search Messages @@ -930,94 +356,6 @@ messageRequest.fetchPrevious(onSuccess: (List list) { - -**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `507` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-search-user-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-search-user-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ### For a Particular Group Conversation @@ -1050,94 +388,6 @@ messageRequest.fetchPrevious(onSuccess: (List list) { - -**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `508` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver group object | [See below ↓](#fetch-search-group-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"group_cometchat-guid-1"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | GUID of the receiver group | `"cometchat-guid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-search-group-sender-object) | -| `receiverType` | string | Type of the receiver | `"group"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ## Unread Messages Count *In other words, as a logged-in user, how do I find out the number of unread messages that I have?* @@ -1389,22 +639,3 @@ CometChat.getUnreadMessageCountForAllGroups(hideMessagesFromBlockedUsers: hideMe In the `onSuccess()` callback, you will receive a `Map` which will contain the `GUIDs` of the groups as the key and the unread message counts as the values. - ---- - -## Next Steps - - - - Track when messages are delivered and read by recipients - - - Show real-time typing status in conversations - - - Allow users to edit previously sent messages - - - Remove messages from conversations - - diff --git a/sdk/flutter/v5/resources-overview.mdx b/sdk/flutter/resources-overview.mdx similarity index 100% rename from sdk/flutter/v5/resources-overview.mdx rename to sdk/flutter/resources-overview.mdx diff --git a/sdk/flutter/retrieve-conversations.mdx b/sdk/flutter/retrieve-conversations.mdx index cb43d8cc9..690d1d42b 100644 --- a/sdk/flutter/retrieve-conversations.mdx +++ b/sdk/flutter/retrieve-conversations.mdx @@ -1,82 +1,20 @@ --- title: "Retrieve Conversations" -sidebarTitle: "Retrieve Conversations" -description: "Fetch, filter, tag, and search conversations using the CometChat Flutter SDK." --- -{/* TL;DR for Agents and Quick Reference */} - -```dart -// Fetch conversations list -ConversationsRequest request = (ConversationsRequestBuilder() - ..limit = 30 -).build(); - -await request.fetchNext( - onSuccess: (List conversations) { - for (Conversation conv in conversations) { - debugPrint("Conversation: ${conv.conversationId}"); - } - }, - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Get a specific conversation -CometChat.getConversation("UID", "user", - onSuccess: (Conversation conv) => debugPrint("Got: ${conv.conversationId}"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Tag a conversation -CometChat.tagConversation("UID", "user", ["archived"], - onSuccess: (Conversation conv) => debugPrint("Tagged: ${conv.conversationId}"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Convert message to conversation -Conversation conversation = CometChat.getConversationFromMessage(message); -``` - - -Conversations provide the last message for every one-on-one and group conversation the logged-in user is part of. Each [`Conversation`](/sdk/reference/entities#conversation) object includes the other participant (user or group), the last message, unread counts, and optional tags. Use this to build a Recent Chats list. ## Retrieve List of Conversations - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - - *In other words, as a logged-in user, how do I retrieve the latest conversations that I've been a part of?* To fetch the list of conversations, you can use the `ConversationsRequest` class. To use this class i.e. to create an object of the `ConversationsRequest` class, you need to use the `ConversationsRequestBuilder` class. The `ConversationsRequestBuilder` class allows you to set the parameters based on which the conversations are to be fetched. -Fetching using this builder will return [`Conversation`](/sdk/reference/entities#conversation) objects. - -### ConversationsRequestBuilder Parameters - -The `ConversationsRequestBuilder` to fetch conversations with various filters. - -| Property | Type | Description | -|----------|------|-------------| -| `limit` | `int?` | Number of conversations to fetch per request. Default is 30, max is 50. | -| `conversationType` | `String?` | Filter by `CometChatConversationType.user` or `CometChatConversationType.group`. If not set, both types are returned. | -| `withUserAndGroupTags` | `bool?` | Include user/group tags in the response. Default is `false`. | -| `withTags` | `bool?` | Include conversation tags in the response. Default is `false`. | -| `tags` | `List?` | Fetch only conversations matching the specified tags. | -| `userTags` | `List?` | Fetch only user conversations where the user has the specified tags. | -| `groupTags` | `List?` | Fetch only group conversations where the group has the specified tags. | -| `includeBlockedUsers` | `bool?` | Include conversations with blocked users. Default is `false`. | -| `withBlockedInfo` | `bool?` | Include blocked user information (`hasBlockedMe`, `blockedByMe`). Default is `false`. | -| `searchKeyword` | `String?` | Search conversations by user or group name. Requires Conversation & Advanced Search. | -| `unread` | `bool?` | Fetch only conversations with unread messages. Requires Conversation & Advanced Search. Default is `false`. | -| `setPage` | `int?` | Fetch conversations from a particular page. | -| `hideAgentic` | `bool?` | Exclude AI agent conversations from results. Default is `false`. | -| `onlyAgentic` | `bool?` | Fetch only AI agent conversations. Default is `false`. | +The `ConversationsRequestBuilder` class allows you to set the below parameters: ### Set Limit -Set the number of conversations to fetch per request. +This method sets the limit i.e. the number of conversations that should be fetched in a single iteration. @@ -85,16 +23,19 @@ ConversationsRequest conversationRequest = (ConversationsRequestBuilder() ..limit = 50 ).build(); ``` + - - -The default value for `limit` is 30 and the max value is 50. - + ### Set Conversation Type -Filter by conversation type: `user` for one-on-one or `group` for group conversations. If not set, both types are returned. +This method can be used to fetch user or group conversations specifically. The `conversationType` variable can hold one of the below two values: + +* user - Only fetches user conversation. +* group - Only fetches group conversations. + +If none is set, the list of conversations will include both user and group conversations. @@ -104,14 +45,14 @@ ConversationsRequest conversationRequest = (ConversationsRequestBuilder() ..conversationType = CometChatConversationType.user ).build(); ``` + - -When conversations are fetched successfully, the response will include an array of [`Conversation`](/sdk/reference/entities#conversation) objects filtered by the specified type. + ### With User and Group Tags -Use `withUserAndGroupTags = true` to include user/group tags in the `Conversation` object. Default is `false`. +This method can be used to fetch the user/group tags in the `Conversation` Object. By default the value is false. @@ -121,48 +62,14 @@ ConversationsRequest conversationRequest = (ConversationsRequestBuilder() ..withUserAndGroupTags = true ).build(); ``` - - - -When conversations are fetched successfully, the response will include `tags` arrays on the `conversationWith` objects (user or group). - -### Set User Tags -Fetch user conversations where the user has specific tags. - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..userTags = ["tag1"] -).build(); -``` - - -When conversations are fetched successfully, the response will include only user conversations where the user has the specified tags. - -### Set Group Tags - -Fetch group conversations where the group has specific tags. - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..groupTags = ["tag1"] -).build(); -``` - -When conversations are fetched successfully, the response will include only group conversations where the group has the specified tags. - ### With Tags -Use `withTags = true` to include conversation tags in the response. Default is `false`. +This method makes sure that the tags associated with the conversations are returned along with the other details of the conversations. The default value for this parameter is `false` @@ -172,12 +79,14 @@ ConversationsRequest conversationRequest = (ConversationsRequestBuilder() ..withTags = true ).build(); ``` + + ### Set Tags -Fetch conversations that have specific tags. +This method helps you fetch the conversations based on the specified tags. @@ -189,12 +98,14 @@ ConversationsRequest conversationRequest = (ConversationsRequestBuilder() ..tags = tags ).build(); ``` + + ### Include Blocked Users -Use `includeBlockedUsers = true` to include conversations with users you've blocked. +This method helps you fetch the conversations of users whom the logged-in user has blocked. @@ -204,14 +115,14 @@ ConversationsRequest conversationRequest = (ConversationsRequestBuilder() ..includeBlockedUsers = true ).build(); ``` + - -When conversations are fetched successfully, the response includes conversations with blocked users. To also get blocked info details (`blockedByMe`, `hasBlockedMe`), set `withBlockedInfo` to `true`. + ### With Blocked Info -Use `withBlockedInfo = true` to include blocked user information in the response. +This method can be used to fetch the blocked information of the blocked user in the `ConversationWith` object. @@ -221,12 +132,14 @@ ConversationsRequest conversationRequest = (ConversationsRequestBuilder() ..withBlockedInfo = true ).build(); ``` + + ### Search Conversations -Use `searchKeyword` to search conversations by user or group name. +This method helps you search a conversation based on User or Group name. @@ -242,14 +155,14 @@ This feature is only available with `Conversation & Advanced Search`. The `Conve ..searchKeyword = "Hiking" ).build(); ``` + - -When conversations are fetched successfully, the response includes conversations where the user or group name matches the search keyword. + ### Unread Conversations -Use `unread = true` to fetch only conversations with unread messages. +This method helps you fetch unread conversations. @@ -265,228 +178,108 @@ This feature is only available with `Conversation & Advanced Search`. The `Conve ..unread = true ).build(); ``` + - -When conversations are fetched successfully, the response includes only conversations with unread messages (`unreadMessageCount` > 0). + -### Hide Agentic Conversations +### Filter by User Tags -Use `hideAgentic = true` to exclude AI agent conversations from the list. +This method helps you filter conversations based on specific user tags. ```dart +List userTags = ["premium", "verified"]; ConversationsRequest conversationRequest = (ConversationsRequestBuilder() ..limit = 50 - ..hideAgentic = true + ..userTags = userTags ).build(); ``` + + -### Only Agentic Conversations +### Filter by Group Tags -Use `onlyAgentic = true` to fetch only AI agent conversations. +This method helps you filter conversations based on specific group tags. ```dart +List groupTags = ["support", "sales"]; ConversationsRequest conversationRequest = (ConversationsRequestBuilder() ..limit = 50 - ..onlyAgentic = true + ..groupTags = groupTags ).build(); ``` - - - -`hideAgentic` and `onlyAgentic` are mutually exclusive — use only one per request. - + -When conversations are fetched successfully, the response will include only conversations with AI agents. Agent users have `role: "@agentic"` and include agent-specific metadata. + -### Fetch Conversations +### Hide Agentic Conversations -After configuring the builder, call `build()` to create the request, then `fetchNext()` to retrieve conversations. Maximum 50 per request. Call `fetchNext()` repeatedly on the same object to paginate. +This method helps you hide AI-driven (agentic) conversations from the list. Agentic conversations are those involving AI agents. ```dart ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ).build(); - -conversationRequest.fetchNext( - onSuccess: (List conversations){ - - - }, onError: (CometChatException e){ - - }); + ..limit = 50 + ..hideAgentic = true +).build(); ``` - - - - -**On Success** — A `List` containing conversation objects with their associated user/group and last message details: - - -**Conversation Object (per item in list):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `conversationType` | string | Type of conversation | `"user"` | -| `conversationWith` | object | User or Group the conversation is with | [See below ↓](#fetch-conversations-conversationwith-user-object) | -| `lastMessage` | object | Last message in the conversation | [See below ↓](#fetch-conversations-lastmessage-object) | -| `updatedAt` | number | Epoch timestamp of last update | `1745554729` | -| `unreadMessageCount` | number | Count of unread messages | `2` | -| `tags` | array | Tags associated with the conversation | `[]` | -| `unreadMentionsCount` | number | Count of unread mentions | `0` | -| `lastReadMessageId` | number | ID of the last read message | `398` | -| `latestMessageId` | number | ID of the latest message | `401` | + ---- + - - -**`conversationWith` Object (User):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-2"` | -| `name` | string | Display name of the user | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | +### Only Agentic Conversations ---- +This method helps you fetch only AI-driven (agentic) conversations. This is useful when you want to display a separate list of AI agent conversations. - - -**`conversationWith` Object (Group):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `guid` | string | Unique identifier of the group | `"cometchat-guid-1"` | -| `name` | string | Display name of the group | `"Flutter Devs"` | -| `icon` | string | Group icon URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp"` | -| `description` | string | Group description | `"A group for Flutter developers"` | -| `membersCount` | number | Number of members in the group | `5` | -| `metadata` | object | Custom metadata | `{}` | -| `joinedAt` | number | Epoch timestamp when the user joined | `1745500000` | -| `hasJoined` | boolean | Whether the current user has joined | `true` | -| `createdAt` | number | Epoch timestamp when the group was created | `1745400000` | -| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | -| `updatedAt` | number | Epoch timestamp of last update | `1745554729` | -| `tags` | array | Group tags | `[]` | -| `type` | string | Group type | `"public"` | -| `scope` | string | Scope of the current user in the group | `"admin"` | -| `password` | string | Group password (for password-protected groups) | `null` | -| `isBannedFromGroup` | boolean | Whether the current user is banned | `false` | + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..onlyAgentic = true +).build(); +``` ---- + - - -**`lastMessage` Object (TextMessage):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `401` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-conversations-lastmessage-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-conversations-lastmessage-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `1745554730` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `text` | string | The text content of the message | `"Hey, are you available for a call?"` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `moderationStatus` | string | Moderation status of the message | `null` | -| `quotedMessageId` | number | ID of the quoted message | `null` | + ---- +Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `ConversationsRequest` class. - - -**`lastMessage.sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | +Once you have the object of the `ConversationsRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `Conversation` objects containing X number of conversations depending on the limit set. ---- +A Maximum of only 50 Conversations can be fetched at once. - + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ).build(); -**`lastMessage.receiver` Object:** +conversationRequest.fetchNext( + onSuccess: (List conversations){ -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - + }, onError: (CometChatException e){ - + } +``` -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while retrieving conversations."` | + - + -The `Conversation` object consists of the below fields: +The `Conversation`object consists of the below fields: | Field | Information | | --------------------- | -------------------------------------------------------------------- | @@ -496,19 +289,22 @@ The `Conversation` object consists of the below fields: | `conversationWith` | `User` or `Group` object containing the details of the user or group | | `unreadMessageCount` | Unread message count for the conversation | | `unreadMentionsCount` | the count of unread mentions in the conversation. | -| `lastReadMessageId` | the ID of the last read message in the conversation. | +| `lastReadMessageId` | the ID of the last read message in the conversation (int type). | ## Tag Conversation *In other words, as a logged-in user, how do I tag a conversation?* -Use `tagConversation()` to add tags to a conversation. +In order to tag a specific conversation, you can use the `tagConversation()` method. The `tagConversation()` method accepts three parameters. + +1. `conversationWith`: UID/GUID of the user/group whose conversation you want to fetch. -| Parameter | Description | -| --- | --- | -| `conversationWith` | UID or GUID of the user/group | -| `conversationType` | `user` or `group` | -| `tags` | Array of tags to add | +2. `conversationType`: The `conversationType` variable can hold one of the below two values: + + 1. user - Only fetches user conversation. + 2. group - Only fetches group conversations. + +3. `tags`: The `tags` variable will be a list of tags you want to add to a conversation. @@ -526,141 +322,10 @@ CometChat.tagConversation(conversationWith, conversationType, tags, } ); ``` - - - - -**On Success** — A `Conversation` object containing the updated conversation with the applied tags: - - - -**Conversation Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `conversationType` | string | Type of conversation | `"user"` | -| `conversationWith` | object | User or Group the conversation is with | [See below ↓](#tag-conversation-conversationwith-user-object) | -| `lastMessage` | object | Last message in the conversation | [See below ↓](#tag-conversation-lastmessage-object) | -| `updatedAt` | number | Epoch timestamp of last update | `1745554729` | -| `unreadMessageCount` | number | Count of unread messages | `0` | -| `tags` | array | Tags associated with the conversation | `["archived"]` | -| `unreadMentionsCount` | number | Count of unread mentions | `0` | -| `lastReadMessageId` | number | ID of the last read message | `398` | -| `latestMessageId` | number | ID of the latest message | `401` | - ---- - - - -**`conversationWith` Object (User):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | ---- - - - -**`lastMessage` Object (TextMessage):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `401` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#tag-conversation-lastmessage-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#tag-conversation-lastmessage-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `1745554730` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `text` | string | The text content of the message | `"Hey, are you available for a call?"` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `moderationStatus` | string | Moderation status of the message | `null` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`lastMessage.sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`lastMessage.receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to tag the conversation."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while tagging the conversation."` | + - + @@ -672,12 +337,13 @@ The tags for conversations are one-way. This means that if user A tags a convers *In other words, as a logged-in user, how do I retrieve a specific conversation?* -Use `getConversation()` to fetch a specific conversation. +In order to fetch a specific conversation, you can use the `getConversation` method. The `getConversation` method accepts two parameters. -| Parameter | Description | -| --- | --- | -| `conversationWith` | UID or GUID of the user/group | -| `conversationType` | `user` or `group` | +1. `conversationWith`: UID/GUID of the user/group whose conversation you want to fetch. +2. `conversationType`: The `conversationType` variable can hold one of the below two values: + +* user - Only fetches user conversation. +* group - Only fetches group conversations. @@ -692,141 +358,10 @@ CometChat.getConversation(conversationWith, conversationType, } ); ``` - - - -**On Success** — A `Conversation` object containing the details of the requested conversation: - - - -**Conversation Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `conversationType` | string | Type of conversation | `"user"` | -| `conversationWith` | object | User or Group the conversation is with | [See below ↓](#get-conversation-conversationwith-user-object) | -| `lastMessage` | object | Last message in the conversation | [See below ↓](#get-conversation-lastmessage-object) | -| `updatedAt` | number | Epoch timestamp of last update | `1745554729` | -| `unreadMessageCount` | number | Count of unread messages | `0` | -| `tags` | array | Tags associated with the conversation | `[]` | -| `unreadMentionsCount` | number | Count of unread mentions | `0` | -| `lastReadMessageId` | number | ID of the last read message | `398` | -| `latestMessageId` | number | ID of the latest message | `401` | - ---- - - - -**`conversationWith` Object (User):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`lastMessage` Object (TextMessage):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `401` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#get-conversation-lastmessage-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#get-conversation-lastmessage-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `1745554730` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `text` | string | The text content of the message | `"Hey, are you available for a call?"` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `moderationStatus` | string | Moderation status of the message | `null` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`lastMessage.sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`lastMessage.receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while retrieving the conversation."` | + - + ## Convert Messages to Conversations @@ -837,28 +372,13 @@ As per our [Receive Messages](/sdk/flutter/receive-messages) guide, for real-tim ```dart Conversation conversation = CometChat.getConversationFromMessage(message); ``` + + - -While converting a `Message` object to a `Conversation` object, the `unreadMessageCount` & `tags` will not be available in the `Conversation` object. The unread message count needs to be managed in your client-side code. - + ---- +While converting `Message` object to `Conversation` object, the `unreadMessageCount` & `tags` will not be available in the `Conversation` object. The unread message count needs to be managed in your client-side code. -## Next Steps - - - - Remove conversations from the logged-in user's list - - - Show real-time typing status in conversations - - - Track message delivery and read status - - - Listen for incoming messages in real-time - - + diff --git a/sdk/flutter/retrieve-group-members.mdx b/sdk/flutter/retrieve-group-members.mdx index 1eaa8a1ba..517799414 100644 --- a/sdk/flutter/retrieve-group-members.mdx +++ b/sdk/flutter/retrieve-group-members.mdx @@ -1,65 +1,17 @@ --- title: "Retrieve Group Members" -sidebarTitle: "Retrieve Members" -description: "Fetch and filter group members by scope, status, and search keyword using the CometChat Flutter SDK with pagination support." --- - -```dart -// Retrieve group members with pagination -GroupMembersRequest request = (GroupMembersRequestBuilder("GROUP_ID") - ..limit = 30 -).build(); - -await request.fetchNext( - onSuccess: (List members) { - for (GroupMember member in members) { - debugPrint("Member: ${member.name}, Scope: ${member.scope}"); - } - }, - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Filter by scope (admin, moderator, participant) -GroupMembersRequest scopedRequest = (GroupMembersRequestBuilder("GROUP_ID") - ..limit = 30 - ..scopes = ["admin", "moderator"] -).build(); - -// Search members -GroupMembersRequest searchRequest = (GroupMembersRequestBuilder("GROUP_ID") - ..limit = 30 - ..searchKeyword = "john" -).build(); -``` - - -Fetch the members of a group with filtering by scope, online status, and search keyword. Results are returned as [`GroupMember`](/sdk/reference/entities#groupmember) objects, which extend [`User`](/sdk/reference/entities#user) with group-specific fields like scope. ## Retrieve the List of Group Members -In order to fetch the list of groups members for a group, you can use the `GroupMembersRequest` class. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +In order to fetch the list of groups members for a group, you can use the `GroupMembersRequest` class. To use this class i.e to create an object of the `GroupMembersRequest` class, you need to use the `GroupMembersRequestBuilder` class. The `GroupMembersRequestBuilder` class allows you to set the parameters based on which the groups are to be fetched. -To use this class i.e to create an object of the `GroupMembersRequest` class, you need to use the `GroupMembersRequestBuilder` class. The `GroupMembersRequestBuilder` class allows you to set the parameters based on which the groups are to be fetched. +The `GroupMembersRequestBuilder` class allows you to set the below parameters: The `GUID` of the group for which the members are to be fetched must be specified in the constructor of the `GroupMembersRequestBuilder` class. -### GroupMembersRequestBuilder - -| Parameter | Type | Description | -|-----------|------|-------------| -| `guid` | `String` | **(Required, constructor)** Group ID for the group whose members are to be fetched. | -| `limit` | `int?` | Maximum number of members to fetch per request. Max `100`, default `30`. | -| `searchKeyword` | `String?` | Search string to filter members by name. | -| `scopes` | `List?` | Filter members by scope (`"admin"`, `"moderator"`, `"participant"`). | -| `status` | `String?` | Filter members by online status (`"online"`, `"offline"`). If not set, returns all members. | -| `setPage` | `int?` | Fetch group members from a particular page number. | - ### Set Limit This method sets the limit i.e. the number of members that should be fetched in a single iteration. @@ -116,22 +68,9 @@ GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) -Relevant fields to access on returned members: - -| Field | Property | Return Type | Description | -|-------|----------|-------------|-------------| -| scope | `scope` | `String` | Scope of the member in the group (`"admin"`, `"moderator"`, or `"participant"`) | - ### Set Status -Filters members by online status: - -| Value | Description | -|-------|-------------| -| `"online"` | Only online members | -| `"offline"` | Only offline members | - -If not set, returns all members regardless of status. +This method allows you to filter group members based on their online/offline status. @@ -139,14 +78,20 @@ If not set, returns all members regardless of status. String GUID = "GUID"; GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) ..limit = 20 - ..status = "online" -).build(); + ..status = CometChatUserStatus.online // or CometChatUserStatus.offline + ).build(); ``` +The `status` parameter can contain one of the below two values: +* `CometChatUserStatus.online` - will return only online group members. +* `CometChatUserStatus.offline` - will return only offline group members. + +If this parameter is not set, all group members will be returned regardless of their status. + Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `GroupMembersRequest` class. Once you have the object of the `GroupMembersRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `GroupMember` objects containing N number of members depending on the limit set. @@ -169,54 +114,3 @@ groupMembersRequest.fetchNext(onSuccess: (List groupMemberList){ - -The `fetchNext()` method returns a list of [`GroupMember`](/sdk/reference/entities#groupmember) objects. `GroupMember` extends [`User`](/sdk/reference/entities#user) and adds group-specific fields. - - -**On Success** — A `List` containing the group members for the specified group (each item is a `GroupMember` object): - - - -**GroupMember Object (per item in array):** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | -| `scope` | string | Member scope in the group | `"admin"` | -| `joinedAt` | number | Epoch timestamp when the member joined the group | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified group does not exist."` | -| `details` | string | Additional technical details | `"Please verify the group ID and try again."` | - - - ---- - -## Next Steps - - - - Add new members to your groups - - - Remove or ban members from groups - - diff --git a/sdk/flutter/retrieve-groups.mdx b/sdk/flutter/retrieve-groups.mdx index d5182cea2..23417e0b0 100644 --- a/sdk/flutter/retrieve-groups.mdx +++ b/sdk/flutter/retrieve-groups.mdx @@ -1,53 +1,8 @@ --- title: "Retrieve Groups" -sidebarTitle: "Retrieve Groups" -description: "Fetch, filter, and search groups using the CometChat Flutter SDK. Includes pagination, tag-based filtering, joined-only groups, and online member counts." --- - -```dart -// Retrieve groups with pagination -GroupsRequest request = (GroupsRequestBuilder() - ..limit = 30 - ..searchKeyword = "search_term" -).build(); - -await request.fetchNext( - onSuccess: (List groups) { - for (Group group in groups) { - debugPrint("Group: ${group.name}"); - } - }, - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Get a specific group by GUID -await CometChat.getGroup( - "GROUP_ID", - onSuccess: (Group group) => debugPrint("Group: ${group.name}"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); - -// Fetch only joined groups -GroupsRequest joinedRequest = (GroupsRequestBuilder() - ..limit = 30 - ..joinedOnly = true -).build(); - -// Get online member count -CometChat.getOnlineGroupMemberCount(["GUID"], - onSuccess: (Map count) => debugPrint("Count: $count"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); -``` - - -Retrieve groups allows you to fetch the list of groups you've joined and groups that are available, as well as get details for a specific group. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - ## Retrieve List of Groups @@ -55,22 +10,9 @@ Retrieve groups allows you to fetch the list of groups you've joined and groups In order to fetch the list of groups, you can use the `GroupsRequest` class. To use this class i.e to create an object of the `GroupsRequest` class, you need to use the `GroupsRequestBuilder` class. The `GroupsRequestBuilder` class allows you to set the parameters based on which the groups are to be fetched. -Use `GroupsRequestBuilder` to fetch groups with filtering, searching, and pagination. - -### GroupsRequestBuilder - -| Parameter | Type | Description | -|-----------|------|-------------| -| `limit` | `int?` | Maximum number of groups to fetch per request. Max `100`, default `30`. | -| `searchKeyword` | `String?` | Search string to filter groups by name. | -| `joinedOnly` | `bool?` | When `true`, returns only groups the logged-in user has joined. Default `false`. | -| `tags` | `List?` | List of tags to filter groups by. Only groups with the specified tags are returned. | -| `withTags` | `bool?` | When `true`, includes tag data in the returned group objects. Default `false`. | -| `setPage` | `int?` | Fetch groups from a particular page number. | - ### Set Limit -Sets the number of groups to fetch per request. +This method sets the limit i.e. the number of groups that should be fetched in a single iteration. @@ -86,7 +28,7 @@ GroupsRequest groupsRequest = (GroupsRequestBuilder() ### Set Search Keyword -Filters groups by a search string. +This method allows you to set the search string based on which the groups are to be fetched. @@ -103,7 +45,7 @@ GroupsRequest groupsRequest = (GroupsRequestBuilder() ### Joined Only -When `true`, returns only groups the logged-in user has joined or is a part of. +This method when used, will ask CometChat to only return the groups that the user has joined or is a part of. @@ -120,7 +62,7 @@ GroupsRequest groupsRequest = (GroupsRequestBuilder() ### Set Tags -Filters groups by specified tags. The list fetched will only contain the groups that have been tagged with the specified tags. +This method accepts a list of tags based on which the list of groups is to be fetched. The list fetched will only contain the groups that have been tagged with the specified tags. @@ -139,7 +81,7 @@ GroupsRequest groupsRequest = (GroupsRequestBuilder() ### With Tags -When `true`, includes tag data in the returned group objects. +This property when set to true will fetch tags data along with the list of groups. @@ -156,7 +98,7 @@ GroupsRequest groupsRequest = (GroupsRequestBuilder() Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `GroupsRequest` class. -Once you have the object of the `GroupsRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of [`Group`](/sdk/reference/entities#group) objects containing 'n' number of groups depending on the limit set. +Once you have the object of the `GroupsRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `Group` objects containing 'n' number of groups depending on the limit set. The list of groups fetched will only have the public and password type groups. The private groups will only be available if the user is a member of that private group. @@ -178,44 +120,6 @@ groupsRequest.fetchNext(onSuccess: (List groupList) { - -**On Success** — A `List` containing the fetched groups. Each `Group` object has the following structure: - - - -**Group Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | -| `name` | string | Display name of the group | `"Tech Enthusiasts"` | -| `icon` | string | URL of the group icon | `null` | -| `description` | string | Description of the group | `"A group for tech lovers"` | -| `membersCount` | number | Number of members in the group | `12` | -| `metadata` | object | Custom metadata attached to the group | `{}` | -| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | -| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | -| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | -| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | -| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | -| `tags` | array | List of tags associated with the group | `[]` | -| `type` | string | Type of the group (public, private, password) | `"public"` | -| `scope` | string | Scope of the logged-in user in the group | `"admin"` | -| `password` | string | Password for password-protected groups | `null` | -| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ## Retrieve Particular Group Details *In other words, as a logged-in user, how do I retrieve information for a specific group?* @@ -242,45 +146,7 @@ CometChat.getGroup(GUID, onSuccess: (Group group) { | --------- | ------------------------------------------------------------ | | `GUID` | The GUID of the group for whom the details are to be fetched | -On success, the [`Group`](/sdk/reference/entities#group) object containing the details of the group is returned. - - -**On Success** — A `Group` object containing all details of the requested group: - - - -**Group Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | -| `name` | string | Display name of the group | `"Tech Enthusiasts"` | -| `icon` | string | URL of the group icon | `null` | -| `description` | string | Description of the group | `"A group for tech lovers"` | -| `membersCount` | number | Number of members in the group | `12` | -| `metadata` | object | Custom metadata attached to the group | `{}` | -| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | -| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | -| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | -| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | -| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | -| `tags` | array | List of tags associated with the group | `[]` | -| `type` | string | Type of the group (public, private, password) | `"public"` | -| `scope` | string | Scope of the logged-in user in the group | `"admin"` | -| `password` | string | Password for password-protected groups | `null` | -| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified group does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | - - +On success, the `Group` object containing the details of the group is returned. ## Get online group member count @@ -306,41 +172,3 @@ CometChat.getOnlineGroupMemberCount(guids, This method returns a `Map` with the GUID of the group as the key and the online member count for that group as the value. - - -**On Success** — A `Map` containing the GUID of each group as the key and the online member count as the value: - - - -**Map Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `cometchat-guid-1` | number | Online member count for the group | `3` | -| `cometchat-guid-11` | number | Online member count for the group | `7` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - - ---- - -## Next Steps - - - - Create new public, private, or password-protected groups - - - Get the list of members in a group - - diff --git a/sdk/flutter/retrieve-users.mdx b/sdk/flutter/retrieve-users.mdx index 1331d8932..9284b158f 100644 --- a/sdk/flutter/retrieve-users.mdx +++ b/sdk/flutter/retrieve-users.mdx @@ -1,96 +1,34 @@ --- title: "Retrieve Users" -sidebarTitle: "Retrieve Users" -description: "Fetch, filter, search, and sort users using the CometChat Flutter SDK. Includes pagination, role-based filtering, tag support, and online user counts." --- - -```dart -// Fetch users list -UsersRequest usersRequest = (UsersRequestBuilder()..limit = 30).build(); -usersRequest.fetchNext( - onSuccess: (List userList) => debugPrint("Users: $userList"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}") -); - -// Get specific user details -CometChat.getUser("UID", - onSuccess: (User user) => debugPrint("User: $user"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}") -); - -// Get logged-in user -User? user = await CometChat.getLoggedInUser(); - -// Get online user count -CometChat.getOnlineUserCount( - onSuccess: (int count) => debugPrint("Online: $count"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}") -); -``` - - -The CometChat SDK provides methods to retrieve the logged-in user, fetch filtered user lists, look up individual users by UID, and get online user counts. All user methods return [`User`](/sdk/reference/entities#user) objects. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - - -### User Object Fields - -| Field | Type | Description | -|-------|------|-------------| -| `uid` | `String` | Unique user ID | -| `name` | `String` | Display name of the user | -| `avatar` | `String?` | URL of the user's avatar image | -| `status` | `String` | Online status of the user (`"online"` or `"offline"`) | -| `lastActiveAt` | `int?` | Epoch timestamp when the user was last active | -| `role` | `String` | Role assigned to the user | -| `tags` | `List` | Tags associated with the user | ## Retrieve Logged In User Details -Use `getLoggedInUser()` to get the current user's details. Returns `null` if no user is logged in. +You can get the details of the logged-in user using the `getLoggedInUser()` method. This method can also be used to check if the user is logged in or not. If the method returns `null`, it indicates that the user is not logged in and you need to log the user into CometChat. ```dart -User? user = await CometChat.getLoggedInUser(); +User user = await CometChat.getLoggedInUser() ``` + + -This method returns a [`User`](/sdk/reference/entities#user) object with the logged-in user's information. +This method will return a `User` object containing all the information related to the logged-in user. ## Retrieve List of Users -In order to fetch the list of users, you can use the `UsersRequest` class. To use this class i.e to create an object of the `UsersRequest` class, you need to use the `UsersRequestBuilder` class. The `UsersRequestBuilder` class allows you to set the parameters based on which the users are to be fetched. - -Fetching using this builder will return [`User`](/sdk/reference/entities#user) objects. - -### UsersRequestBuilder Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `limit` | `int?` | Number of users to fetch per request | -| `searchKeyword` | `String?` | Filters users by a search string | -| `searchIn` | `List?` | Specifies which user properties to search (`"uid"`, `"name"`). Works with `searchKeyword`. | -| `userStatus` | `String?` | Filters by online status (`CometChatUserStatus.online` or `CometChatUserStatus.offline`) | -| `hideBlockedUsers` | `bool?` | When `true`, excludes users blocked by the logged-in user | -| `roles` | `List?` | Filters users by specified roles | -| `friendsOnly` | `bool?` | When `true`, returns only friends of the logged-in user | -| `tags` | `List?` | Filters users by specified tags | -| `withTags` | `bool?` | When `true`, includes tag data in the returned user objects | -| `uids` | `List?` | Fetches specific users by their UIDs. Maximum 25 per request. | -| `sortBy` | `String?` | Sorts the user list by a specific property. Default sort order: `status → name → UID`. Pass `"name"` to sort by `name → UID`. | -| `sortByOrder` | `String?` | Sets the sort order. Default is ascending (`"asc"`). Use `"desc"` for descending. | +In order to fetch the list of users, you can use the `UsersRequest` class. To use this class i.e to create an object of the UsersRequest class, you need to use the `UsersRequestBuilder` class. The `UsersRequestBuilder` class allows you to set the parameters based on which the users are to be fetched. The `UsersRequestBuilder` class allows you to set the below parameters: ### Set Limit -Sets the number of users to fetch per request. +This method sets the limit i.e. the number of users that should be fetched in a single iteration. @@ -99,12 +37,14 @@ UsersRequest usersRequest = (UsersRequestBuilder() ..limit = 50 ).build(); ``` + + ### Set Search Keyword -Filters users by a search string. +This method allows you to set the search string based on which the users are to be fetched. @@ -114,33 +54,19 @@ UsersRequest usersRequest = (UsersRequestBuilder() ..searchKeyword = "abc" ).build(); ``` - - -### Search In - -Specifies which user properties to search. Works with `searchKeyword`. By default, searches both UID and name. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..searchKeyword = "super" - ..searchIn = ["uid", "name"] - ).build(); -``` + ### Set Status -Filters users by online status: +The status based on which the users are to be fetched. The status parameter can contain one of the below two values: -- `CometChatUserStatus.online` — Only online users -- `CometChatUserStatus.offline` — Only offline users +* CometChatUserStatus.online - will return the list of only online users. +* CometChatUserStatus.offline - will return the list of only offline users. -If not set, returns all users. +If this parameter is not set, will return all the available users. @@ -150,12 +76,16 @@ UsersRequest usersRequest = (UsersRequestBuilder() ..userStatus = CometChatUserStatus.online ).build(); ``` + + +If this parameter is not set, will return all users. + ### Hide Blocked Users -When `true`, excludes users blocked by the logged-in user from the results. +This method is used to determine if the blocked users should be returned as a part of the user list. If set to `true`, the user list will not contain the users blocked by the logged-in user. @@ -165,12 +95,14 @@ UsersRequest usersRequest = (UsersRequestBuilder() ..hideBlockedUsers = true ).build(); ``` + + ### Set Roles -Filters users by specified roles. +This method allows you to fetch the users based on multiple roles. @@ -183,12 +115,14 @@ UsersRequest usersRequest = (UsersRequestBuilder() ..roles = roles ).build(); ``` + + ### Friends Only -When `true`, returns only friends of the logged-in user. +This property when set to true will return only the friends of the logged-in user. @@ -198,12 +132,14 @@ UsersRequest usersRequest = (UsersRequestBuilder() ..friendsOnly = true ).build(); ``` + + ### Set Tags -Filters users by specified tags. +This method accepts a list of tags based on which the list of users is to be fetched. The list fetched will only contain the users that have been tagged with the specified tags. @@ -216,27 +152,34 @@ UsersRequest usersRequest = (UsersRequestBuilder() ..tags = tags ).build(); ``` + + ### With Tags -When `true`, includes tag data in the returned user objects. +This property when set to true will fetch tags data along with the list of users. ```dart +List tags = []; +tags.add("tag1"); +tags.add("tag2"); UsersRequest usersRequest = (UsersRequestBuilder() ..limit = 50 ..withTags = true ).build(); ``` + + ### Set UIDs -Fetches specific users by their UIDs. Maximum 25 users per request. +This method accepts a list of UIDs based on which the list of users is fetched. A maximum of 25 users can be fetched. @@ -249,40 +192,85 @@ UsersRequest usersRequest = (UsersRequestBuilder() ..uids = uids ).build(); ``` + + + + + +### Search In + +This method allows you to specify which user fields to search in when using the search keyword. You can search in `name`, `uid`, or both. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..searchKeyword = "john" + ..searchIn = ["name", "uid"] + ).build(); +``` + + ### Sort By -Sorts the user list by a specific property. Default sort order: `status → name → UID`. Pass `"name"` to sort by `name → UID`. +This method allows you to specify the field by which the users should be sorted. You can sort by `name`, `status`, or `createdAt`. ```dart UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 30 + ..limit = 50 ..sortBy = "name" ).build(); ``` + + ### Sort By Order -Sets the sort order. Default is ascending (`"asc"`). Use `"desc"` for descending. +This method allows you to specify the order in which the users should be sorted. You can use `asc` for ascending or `desc` for descending order. ```dart UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 30 - ..sortByOrder = "desc" + ..limit = 50 + ..sortBy = "name" + ..sortByOrder = "asc" ).build(); ``` + + -After configuring the builder, call `build()` to get the `UsersRequest` object, then call `fetchNext()` to retrieve users. +You can combine these parameters for more precise user filtering and ordering: + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..searchKeyword = "john" + ..searchIn = ["name"] + ..sortBy = "name" + ..sortByOrder = "asc" + ).build(); +``` + + + + + +Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `UsersRequest` class. + +Once you have the object of the `UsersRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `User` objects containing 'n' number of users depending on the limit set. @@ -297,48 +285,14 @@ usersRequest.fetchNext(onSuccess: (List userList){ debugPrint("User List Fetch Failed: ${e.message}"); }); ``` - - - -The `fetchNext()` method returns a list of [`User`](/sdk/reference/entities#user) objects. - - -**On Success** — A list of `User` objects matching the request filters. Each item in the list contains: - - -**User Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + - + ## Retrieve Particular User Details -Use `getUser()` to fetch a specific user's details by UID. +To get the information of a user, you can use the `getUser()` method. @@ -351,7 +305,9 @@ CometChat.getUser(UID, onSuccess: (User user){ debugPrint("User Fetch Failed: ${e.message}"); }); ``` + + The `getUser()` method takes the following parameters: @@ -362,43 +318,9 @@ The `getUser()` method takes the following parameters: On success, the `User` object containing the details of the user is returned. - -**On Success** — A `User` object containing the details of the requested user: - - - -**User Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified UID does not exist."` | -| `details` | string | Additional technical details | `"Please verify the UID and try again."` | +## Get online user count - - -## Get Online User Count - -Use `getOnlineUserCount()` to get the total number of online users in your app. +To get the total count of online users for your app, you can use the `getOnlineUserCount()` method. @@ -409,45 +331,7 @@ CometChat.getOnlineUserCount(onSuccess: (int count){ debugPrint("User Count Fetch Failed: ${e.message}"); }); ``` - - - -`getOnlineUserCount()` resolves with an `int` representing the total count of currently online users in your app. - - -**On Success** — An `int` value representing the total count of online users: - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `count` | number | Total number of online users | `12` | - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ---- + -## Next Steps - - - - Monitor and manage real-time user online/offline status - - - Block and unblock users to control interactions - - - Create, update, and delete users programmatically - - - Explore all user-related features and capabilities - - + diff --git a/sdk/flutter/send-message.mdx b/sdk/flutter/send-message.mdx index 57cc58dfa..fd2c833d9 100644 --- a/sdk/flutter/send-message.mdx +++ b/sdk/flutter/send-message.mdx @@ -1,72 +1,15 @@ --- -title: "Send Messages" -sidebarTitle: "Send Messages" -description: "Send text, media, and custom messages to users and groups using the CometChat Flutter SDK." +title: "Send A Message" --- - -| Field | Value | -| --- | --- | -| Key Classes | [`TextMessage`](/sdk/reference/messages#textmessage), [`MediaMessage`](/sdk/reference/messages#mediamessage), [`CustomMessage`](/sdk/reference/messages#custommessage) | -| Key Methods | `CometChat.sendMessage()`, `CometChat.sendMediaMessage()`, `CometChat.sendCustomMessage()` | -| Receiver Types | `CometChatReceiverType.user`, `CometChatReceiverType.group` | -| Message Types | `CometChatMessageType.text`, `CometChatMessageType.image`, `CometChatMessageType.video`, `CometChatMessageType.audio`, `CometChatMessageType.file`, `CometChatMessageType.custom` | -| Prerequisites | SDK initialized, user logged in | -```dart -// Send text message to user -TextMessage textMessage = TextMessage( - text: "Hello!", - receiverUid: "UID", - receiverType: CometChatReceiverType.user, - type: CometChatMessageType.text, -); -CometChat.sendMessage(textMessage, onSuccess: (msg) {}, onError: (e) {}); - -// Send text message to group -TextMessage groupMessage = TextMessage( - text: "Hello group!", - receiverUid: "GUID", - receiverType: CometChatReceiverType.group, - type: CometChatMessageType.text, -); -CometChat.sendMessage(groupMessage, onSuccess: (msg) {}, onError: (e) {}); +Using CometChat, you can send three types of messages: -// Send media message (image) -MediaMessage mediaMessage = MediaMessage( - receiverUid: "UID", - receiverType: CometChatReceiverType.user, - type: CometChatMessageType.image, - file: "path/to/image.jpg", -); -CometChat.sendMediaMessage(mediaMessage, onSuccess: (msg) {}, onError: (e) {}); - -// Send custom message -CustomMessage customMessage = CustomMessage( - receiverUid: "UID", - receiverType: CometChatReceiverType.user, - type: "location", - customData: {"latitude": "50.6192", "longitude": "-72.6818"}, -); -CometChat.sendCustomMessage(customMessage, onSuccess: (msg) {}, onError: (e) {}); -``` - - - -CometChat supports three types of messages: - -| Type | Method | Use Case | -| --- | --- | --- | -| [Text](#text-message) | `CometChat.sendMessage()` | Plain text messages | -| [Media](#media-message) | `CometChat.sendMediaMessage()` | Images, videos, audio, files | -| [Custom](#custom-message) | `CometChat.sendCustomMessage()` | Location, polls, or any structured data | - -You can also send [Interactive Messages](/sdk/flutter/interactive-messages) for forms, cards, and custom UI elements. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +1. A [Text Message](/sdk/flutter/send-message#text-message), the most common and standard message type. +2. A [Media Message](/sdk/flutter/send-message#media-message), for sending photos, videos and files. +3. A [Custom Message](/sdk/flutter/send-message#custom-message), for sending completely custom data using Map structures. +4. A [Interactive Messages](/sdk/flutter/interactive-messages) , for sending end-user interactive messages of type form, card and custom Interactive You can also send metadata along with a text or media message. Think, for example, if you'd want to share the user's location with every message, you can use the metadata field. @@ -74,7 +17,7 @@ You can also send metadata along with a text or media message. Think, for exampl *In other words, as a sender, how do I send a text message?* -To send a text message to a single user or group, you need to use the `sendMessage()` method and pass a [`TextMessage`](/sdk/reference/messages#textmessage) object to it. +To send a text message to a single user or group, you need to use the `sendMessage()` method and pass a `TextMessage` object to it. ### Add Metadata @@ -167,99 +110,6 @@ The `TextMessage` class constructor takes the following parameters: When a text message is sent successfully, the response will include a `TextMessage` object which includes all information related to the sent message. - -**On Success** — A `TextMessage` object containing all details of the sent message: - - - -**TextMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `401` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#send-text-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#send-text-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `text` | string | The text content of the message | `"messageText"` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `moderationStatus` | string | Moderation status of the message | `null` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_EMPTY_MESSAGE_TEXT"` | -| `message` | string | Human-readable error message | `"The text message body is empty."` | -| `details` | string | Additional technical details | `"Please provide a non-empty text for the message."` | - - - ### Set Quoted Message To set a quoted message for a message, use the `setQuotedMessageId` and `setQuotedMessage` method of the TextMessage class. This method accepts the ID of the message to be quoted. @@ -281,52 +131,42 @@ Once the text message object is ready, you need to use the `sendMessage()` metho ```dart String receiverID = "UID"; -String messageText = "Hello CometChat!"; -String receiverType = CometChatReceiverType.user; -String type = CometChatMessageType.text; - -TextMessage textMessage = TextMessage( - text: messageText, - receiverUid: receiverID, - receiverType: receiverType, - type: type, -); -textMessage.quotedMessageId = 10; - -CometChat.sendMessage(textMessage, - onSuccess: (TextMessage message) { - debugPrint("Message sent successfully: ${message.toString()}"); - }, - onError: (CometChatException e) { - debugPrint("Message sending failed with exception: ${e.message}"); - }, -); + String messageText = "Hello CometChat!"; + String receiverType = CometChatReceiverType.user; + + TextMessage textMessage = + TextMessage(receiverID, messageText, receiverType); + + CometChat.sendMessage( + textMessage, + onSuccess: (TextMessage message) { + print("Message sent successfully: ${message.toString()}"); + }, + onError: (CometChatException e) { + print("Message sending failed with exception: ${e.message}"); + }, + ); ``` ```dart String receiverID = "GUID"; -String messageText = "Hello CometChat!"; -String receiverType = CometChatReceiverType.group; -String type = CometChatMessageType.text; - -TextMessage textMessage = TextMessage( - text: messageText, - receiverUid: receiverID, - receiverType: receiverType, - type: type, -); -textMessage.quotedMessageId = 10; - -CometChat.sendMessage(textMessage, - onSuccess: (TextMessage message) { - debugPrint("Message sent successfully: ${message.toString()}"); - }, - onError: (CometChatException e) { - debugPrint("Message sending failed with exception: ${e.message}"); - }, -); + String messageText = "Hello CometChat!"; + String receiverType = CometChatReceiverType.group; + + TextMessage textMessage = + TextMessage(receiverID, messageText, receiverType); + + CometChat.sendMessage( + textMessage, + onSuccess: (TextMessage message) { + print("Message sent successfully: ${message.toString()}"); + }, + onError: (CometChatException e) { + print("Message sending failed with exception: ${e.message}"); + }, + ); ``` @@ -339,108 +179,15 @@ The `TextMessage` class constructor takes the following parameters: | -------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------- | | `receiverID` | `UID` of the user or `GUID` of the group receiving the message | Required | | `messageText` | The text message | Required | -| `receiverType` | The type of the receiver- `CometChatReceiverType.user` (user) or `CometChatReceiverType.group` (group) | Required | +| `receiverType` | The type of the receiver- `CometChatConstants.RECEIVER_TYPE_USER` (user) or `CometChatConstants.RECEIVER_TYPE_GROUP` (group) | Required | When a text message is sent successfully, the response will include a `TextMessage` object which includes all information related to the sent message. - -**On Success** — A `TextMessage` object containing all details of the sent quoted message: - - - -**TextMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `402` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#send-quoted-text-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554800` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#send-quoted-text-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554800` | -| `text` | string | The text content of the message | `"Hello CometChat!"` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `moderationStatus` | string | Moderation status of the message | `null` | -| `quotedMessageId` | number | ID of the quoted message | `401` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_EMPTY_MESSAGE_TEXT"` | -| `message` | string | Human-readable error message | `"The text message body is empty."` | -| `details` | string | Additional technical details | `"Please provide a non-empty text for the message."` | - - - ## Media Message *In other words, as a sender, how do I send a media message like photos, videos & files?* -To send a media message to any user or group, you need to use the `sendMediaMessage()` method and pass a [`MediaMessage`](/sdk/reference/messages#mediamessage) object to it. +To send a media message to any user or group, you need to use the `sendMediaMessage()` method and pass a `MediaMessage` object to it. ### Add Metadata @@ -487,65 +234,26 @@ mediaMessage.tags = tags; -### Set Quoted Message - -To quote a message in a media message, use the `quotedMessageId` property of the MediaMessage class. - - - -```dart -mediaMessage.quotedMessageId = 10; -``` - - - - - There are 2 ways you can send Media Messages using the CometChat SDK: 1. **By providing the File :** You can directly share the file object while creating an object of the MediaMessage class. When the media message is sent using the sendMediaMessage() method, this file is then uploaded to CometChat servers and the URL of the file is sent in the success response of the sendMediaMessage() function. - -```dart -String receiverID = "cometchat-uid-1"; -String messageType = CometChatMessageType.image; -String receiverType = CometChatConversationType.user; -String filePath = "storage/emulated/0/Download/46.jpg"; -MediaMessage mediaMessage = MediaMessage( - receiverType: receiverType, - type: messageType, - receiverUid: receiverID, - file: filePath, -); - -await CometChat.sendMediaMessage(mediaMessage, - onSuccess: (MediaMessage message) { - debugPrint("Media message sent successfully: ${mediaMessage.metadata}"); - }, onError: (e) { - debugPrint("Media message sending failed with exception: ${e.message}"); - } -); -``` - - - - + ```dart -String receiverID = "cometchat-guid-1"; +String receiverID; String messageType = CometChatMessageType.image; -String receiverType = CometChatConversationType.group; +String receiverType = CometChatConversationType.user ; String filePath = "storage/emulated/0/Download/46.jpg"; MediaMessage mediaMessage = MediaMessage( - receiverType: receiverType, - type: messageType, - receiverUid: receiverID, - file: filePath, -); +receiverType: receiverType, +type: messageType, +receiverUid: receiverID, +file: filePath); await CometChat.sendMediaMessage(mediaMessage, onSuccess: (MediaMessage message) { - debugPrint("Media message sent successfully: ${mediaMessage.metadata}"); + debugPrint("Media message sent successfully:${mediaMessage.metadata}"); }, onError: (e) { debugPrint("Media message sending failed with exception: ${e.message}"); } @@ -565,126 +273,11 @@ The `MediaMessage` class constructor takes the following parameters: | messageType | The type of the message that needs to be sent which, in this case, can be:
1. `CometChatMessageType.image` (image)
2. `CometChatMessageType.video` (video)
3. `CometChatMessageType.audio` (audio)
4. `CometChatMessageType.file` (file) | Required | | receiverType | The type of the receiver to whom the message is to be sent, i.e., `CometChatReceiverType.user` (user) or `CometChatReceiverType.group` (group) | Required | - -**On Success** — A `MediaMessage` object containing all details of the sent media message: - - - -**MediaMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `403` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#send-media-file-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554900` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"image"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#send-media-file-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554900` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `caption` | string | Caption text for the media message | `null` | -| `attachment` | object | File attachment details | [See below ↓](#send-media-file-attachment-object) | -| `file` | string | Local file path | `"storage/emulated/0/Download/46.jpg"` | -| `files` | array | List of additional file paths | `null` | -| `attachments` | array | List of additional attachments | `null` | -| `moderationStatus` | string | Moderation status of the message | `null` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - ---- - - - -**`attachment` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `fileUrl` | string | URL of the uploaded file | `"https://data-us.cometchat.io/assets/images/46.jpg"` | -| `fileName` | string | Name of the file | `"46.jpg"` | -| `fileExtension` | string | File extension | `"jpg"` | -| `fileMimeType` | string | MIME type of the file | `"image/jpeg"` | -| `fileSize` | number | File size in bytes | `24576` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_FILE_TOO_LARGE"` | -| `message` | string | Human-readable error message | `"The file size exceeds the allowed limit."` | -| `details` | string | Additional technical details | `"Maximum allowed file size is 25 MB."` | - - - -2. **By providing the URL of the File:** The second way to send media messages using the CometChat SDK is to provide the SDK with the URL of any file that is hosted on your servers or any cloud storage. To achieve this you will have to make use of the [`Attachment`](/sdk/reference/auxiliary#attachment) class that is available in the MediaMessage class. For more information, you can refer to the below code snippet: +2. **By providing the URL of the File:** The second way to send media messages using the CometChat SDK is to provide the SDK with the URL of any file that is hosted on your servers or any cloud storage. To achieve this you will have to make use of the Attachment class that is available in the MediaMessage class. For more information, you can refer to the below code snippet: - + ```dart -String receiverID = "cometchat-uid-1"; -String messageType = CometChatMessageType.image; -String receiverType = CometChatConversationType.user; - MediaMessage mediaMessage = MediaMessage( receiverType: receiverType, type: messageType, @@ -696,12 +289,12 @@ String fileName = "test"; String fileExtension = "png"; String fileMimeType = "image/png"; -Attachment attach = Attachment(fileUrl, fileName, fileExtension, fileMimeType, null); -mediaMessage.attachment = attach; +Attachment attach = Attachment(fileUrl,fileName,fileExtension,fileMimeType,null); +mediaMessage.attachment= attach; await CometChat.sendMediaMessage(mediaMessage, onSuccess: (MediaMessage message) { - debugPrint("Media message sent successfully: ${mediaMessage}"); + debugPrint( "Media message sent successfully: ${mediaMessage}"); }, onError: (CometChatException e) { debugPrint("Media message sending failed with exception: ${e.message}"); } @@ -710,153 +303,90 @@ await CometChat.sendMediaMessage(mediaMessage, - + + +When a media message is sent successfully, the response will include a `MediaMessage` object which includes all information related to the sent message. + +If you wish to send a caption or some text along with the Media Message, you can use the `caption field` provided by the MediaMessage class. To get and set the caption you can use the `.caption` variable . As with text messages, the metadata field can be used with media messages as well. Any additional information can be passed along with the media message as a `Map`. + +### Send Multiple Media Files + +You can send multiple media files (images, videos, audio, or documents) in a single message. This enables richer and more efficient conversations. + + + ```dart -String receiverID = "cometchat-guid-1"; +String receiverID = "cometchat-uid-1"; +String receiverType = CometChatConversationType.user; String messageType = CometChatMessageType.image; -String receiverType = CometChatConversationType.group; -MediaMessage mediaMessage = MediaMessage( - receiverType: receiverType, - type: messageType, - receiverUid: receiverID, - file: null); - -String fileUrl = "https://pngimg.com/uploads/mario/mario_PNG125.png"; -String fileName = "test"; -String fileExtension = "png"; -String fileMimeType = "image/png"; +// Create a list of file paths +List filePaths = [ + "storage/emulated/0/Download/image1.jpg", + "storage/emulated/0/Download/image2.jpg", + "storage/emulated/0/Download/image3.jpg", +]; -Attachment attach = Attachment(fileUrl, fileName, fileExtension, fileMimeType, null); -mediaMessage.attachment = attach; +MediaMessage mediaMessage = MediaMessage( + receiverType: receiverType, + type: messageType, + receiverUid: receiverID, + files: filePaths, // Use 'files' for multiple files +); -await CometChat.sendMediaMessage(mediaMessage, +await CometChat.sendMediaMessage(mediaMessage, onSuccess: (MediaMessage message) { - debugPrint("Media message sent successfully: ${mediaMessage}"); - }, onError: (CometChatException e) { - debugPrint("Media message sending failed with exception: ${e.message}"); + debugPrint("Multiple media files sent successfully"); + // Access the list of attachments + List? attachments = message.attachments; + for (var attachment in attachments ?? []) { + debugPrint("Attachment URL: ${attachment.fileUrl}"); + } + }, + onError: (CometChatException e) { + debugPrint("Media message sending failed: ${e.message}"); } -); +); ``` -When a media message is sent successfully, the response will include a `MediaMessage` object which includes all information related to the sent message. - - -**On Success** — A `MediaMessage` object containing all details of the sent media message (via URL): - - - -**MediaMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `404` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#send-media-url-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745555000` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"image"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#send-media-url-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745555000` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `caption` | string | Caption text for the media message | `null` | -| `attachment` | object | File attachment details | [See below ↓](#send-media-url-attachment-object) | -| `file` | string | Local file path | `null` | -| `files` | array | List of additional file paths | `null` | -| `attachments` | array | List of additional attachments | `null` | -| `moderationStatus` | string | Moderation status of the message | `null` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - ---- - - +### File Size and Count Validation -**`attachment` Object:** +The SDK automatically validates file size and count when sending media messages. You can configure these limits through Settings in the CometChat Dashboard. -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `fileUrl` | string | URL of the file | `"https://pngimg.com/uploads/mario/mario_PNG125.png"` | -| `fileName` | string | Name of the file | `"test"` | -| `fileExtension` | string | File extension | `"png"` | -| `fileMimeType` | string | MIME type of the file | `"image/png"` | -| `fileSize` | number | File size in bytes | `null` | +| Validation | Description | +|------------|-------------| +| File Size | Maximum size per file (configurable in Dashboard) | +| File Count | Maximum number of files per message (configurable in Dashboard) | - +If validation fails, the `onError` callback will be triggered with details about the validation error. - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_INVALID_MESSAGE_TYPE"` | -| `message` | string | Human-readable error message | `"The message type provided is not supported."` | -| `details` | string | Additional technical details | `"Supported types are image, video, audio, and file."` | + + +```dart +await CometChat.sendMediaMessage(mediaMessage, + onSuccess: (MediaMessage message) { + debugPrint("Media message sent successfully"); + }, + onError: (CometChatException e) { + // Handle validation errors + if (e.code == "ERR_FILE_SIZE_EXCEEDED") { + debugPrint("File size exceeds the allowed limit"); + } else if (e.code == "ERR_FILE_COUNT_EXCEEDED") { + debugPrint("Too many files in a single message"); + } else { + debugPrint("Error: ${e.message}"); + } + } +); +``` - +
-If you wish to send a caption or some text along with the Media Message, you can use the `caption field` provided by the MediaMessage class. To get and set the caption you can use the `.caption` variable . As with text messages, the metadata field can be used with media messages as well. Any additional information can be passed along with the media message as a `Map`. +
## Custom Message @@ -866,7 +396,7 @@ CometChat allows you to send custom messages which are neither text nor media me In order to send a custom message, you need to use the `sendCustomMessage()` method. -The `sendCustomMessage()` methods takes an object of the [`CustomMessage`](/sdk/reference/messages#custommessage) which can be obtained using the below constructor. +The `sendCustomMessage()` methods takes an object of the `CustomMessage` which can be obtained using the below constructor. @@ -885,35 +415,15 @@ CustomMessage customMessage = CustomMessage( receiverUid: UID, The above constructor, helps you create a custom message with the message type set to whatever is passed to the constructor and the category set to `custom`. -The `CustomMessage` class constructor takes the following parameters: +The parameters involved are: -| Parameter | Description | Required | -| --- | --- | --- | -| `receiverUid` | UID of the user or GUID of the group to which the message is to be sent | Yes | -| `receiverType` | Type of the receiver — `CometChatConversationType.user` or `CometChatConversationType.group` | Yes | -| `type` | Custom message type string (e.g., `"location"`, `"poll"`) | Yes | -| `customData` | The data to be passed as the message in the form of a `Map` | Yes | -| `subType` | Optional sub-type for the custom message | No | +1. `receiverUid` - Unique id of the user or group to which the message is to be sent. +2. `receiverType` - Type of the receiver i.e user or group +3. `customType` - custom message type that you need to set +4. `customData` - The data to be passed as the message in the form of a `Map`. You can also use the subType field of the `CustomMessage` class to set a specific type for the custom message. This can be achieved using the `subtype` field. -### Add Metadata - -To send custom data along with a custom message, you can use the `metadata` property and pass a `Map` to it. - - - -```dart -Map metadata = {}; -metadata["priority"] = "high"; -metadata["source"] = "mobile"; -customMessage.metadata = metadata; -``` - - - - - ### Add Tags To add a tag to a message you can assign value in `.tags` variable of the CustomMessage Class. `tags` accepts a list of tags. @@ -930,20 +440,6 @@ textMessage.tags = tags; -### Set Quoted Message - -To quote a message in a custom message, use the `quotedMessageId` property of the CustomMessage class. - - - -```dart -customMessage.quotedMessageId = 10; -``` - - - - - Once the object of `CustomMessage` class is ready you can send the custom message using the `sendCustomMessage()` method. @@ -1009,101 +505,6 @@ The above sample explains how custom messages can be used to share the location On success, you will receive an object of `CustomMessage` class. - -**On Success** — A `CustomMessage` object containing all details of the sent custom message: - - - -**CustomMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `405` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#send-custom-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745555100` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"custom"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#send-custom-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"custom"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745555100` | -| `customData` | object | Custom data payload | `{"latitude": "19.0760", "longitude": "72.8777"}` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `text` | string | Conversation text for notifications | `null` | -| `updateConversation` | boolean | Whether to update the conversation's last message | `false` | -| `sendNotification` | boolean | Whether to send a push notification | `false` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to send the custom message."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ### Update Conversation *How can I decide whether the custom message should update the last message of a conversation?* @@ -1243,22 +644,3 @@ CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { It is also possible to send interactive messages from CometChat , to know more [click here](/sdk/flutter/interactive-messages)
- ---- - -## Next Steps - - - - Handle incoming messages in real-time - - - Modify sent messages after delivery - - - Send forms, cards, and interactive elements - - - Remove messages from conversations - - diff --git a/sdk/flutter/setup.mdx b/sdk/flutter/setup.mdx index 877c9e0e6..e01fa4627 100644 --- a/sdk/flutter/setup.mdx +++ b/sdk/flutter/setup.mdx @@ -1,75 +1,53 @@ --- title: "Setup" -sidebarTitle: "Setup" -description: "Install, configure, and initialize the CometChat Flutter SDK in your application." --- -{/* TL;DR for Agents and Quick Reference */} - - -```yaml -# Install (pubspec.yaml) -cometchat_sdk: ^4.0.33 -``` - -```dart -import 'package:cometchat_sdk/cometchat_sdk.dart'; - -AppSettings appSettings = (AppSettingsBuilder() - ..subscriptionType = CometChatSubscriptionType.allUsers - ..region = "APP_REGION" - ..autoEstablishSocketConnection = true -).build(); + + -CometChat.init("APP_ID", appSettings, - onSuccess: (msg) => debugPrint("Init success"), - onError: (e) => debugPrint("Init failed: ${e.message}") -); +### Get your Application Keys -// Login (dev only) -CometChat.login("UID", "AUTH_KEY", - onSuccess: (user) => debugPrint("Login success"), - onError: (e) => debugPrint("Login failed: ${e.message}") -); -``` - -**Prerequisites:** Flutter SDK 1.2+, Android API Level 21+, iOS 11+ -**Credentials:** App ID, Region, Auth Key (dev) or Auth Token (prod) from [CometChat Dashboard](https://app.cometchat.com) - +[Signup for CometChat](https://app.cometchat.com/) and then: -## Prerequisites +1. Create a new app +2. Head over to the **API Keys** section and note the **Auth Key**, **App ID** & **Region** -| Requirement | Version | -|-------------|---------| -| Flutter SDK | 1.2 or higher | -| Android API Level | 21 or higher | -| AndroidX | Required | -| iOS | 11 or higher | + +Minimum Requirement -Get your credentials from the [CometChat Dashboard](https://app.cometchat.com): +* Android API Level 21 +* AndroidX Compatibility +* iOS 11 or higher +* Flutter SDK 1.2 or higher -1. Create a new app -2. Head over to the **API & Auth Keys** section and note the **Auth Key**, **App ID** & **Region** + - -**Auth Key** is for development/testing only. In production, generate **Auth Tokens** on your server using the REST API. Never expose Auth Keys in production client code. - +### Add the CometChat Dependency -## Installation +### Cloudsmith -Add the following dependency to your `pubspec.yaml` file and run `pub get`: +Add the Cloudsmith hosted repository and dependency to your `pubspec.yaml`: - - ```yaml -cometchat_sdk: ^4.0.33 +dependencies: + cometchat_sdk: + hosted: + url: https://dart.cloudsmith.io/cometchat/cometchat/ + version: 5.0.0 ``` - - -### iOS Setup +Then run: +```bash +flutter pub get +``` -1. Add the following to your Podfile inside the iOS section of your app: + +**Upgrading from v4?** + +If you're migrating an existing app from CometChat SDK v4, check out the [Upgrading from v4](/sdk/flutter/v5/upgrading-from-v4-guide) guide for breaking changes, deprecated methods, and migration instructions. + + +2. Add following code to podfile inside iOS section of your app @@ -94,40 +72,54 @@ end end ``` + + **Apple Silicon (M1/M2/M3) users:** Excluding `arm64` from the simulator build prevents the app from running natively on Apple Silicon Macs. If you are developing on an Apple Silicon Mac, consider excluding only `i386` instead of `arm64 i386` to enable native simulator builds. -2. Change the deployment target to `11` or higher. -3. Navigate to your iOS folder in terminal and run `pod install`. For Apple Silicon systems, use a Rosetta terminal. -4. Set **Enabled Bitcode** to **NO** in the Build Settings of your Xcode project. +3. For iOS, change the deployment target to `11` or higher. +4. For iOS, navigate to your iOS folder in terminal or CMD and do `pod install` . For apple chip system use rosetta terminal. +5. For iOS you can set the Enabled Bitcode settings to **NO** present in build settings of XCODE project -### Import the SDK - +Import CometChat SDK using following code in dart ```dart import 'package:cometchat_sdk/cometchat_sdk.dart'; ``` + + -## Initialization +## Initialise CometChat + +The `init()` method initialises the settings required for CometChat. The `init()` method takes the below parameters: + +1. appID - You CometChat App ID +2. appSettings - An object of the AppSettings class can be created using the AppSettingsBuilder class. The region field is mandatory and can be set using the `setRegion()` method. -The `init()` method initializes the SDK and must be called before any other CometChat method. Call it once at app startup, typically in your root widget's `initState()` or `main()` function. +The `AppSettings` class allows you to configure three settings: + +* **Region**: The region where you app was created. +* **[Presence Subscription](/sdk/flutter/user-presence):** Represents the subscription type for user presence (real-time online/offline status) +* **autoEstablishSocketConnection(boolean value)**: This property takes a boolean value which when set to true informs the SDK to manage the web-socket connection internally. If set to false, it informs the SDK that the web-socket connection will be managed manually. The default value for this parameter is true. For more information on this, please check the [Connection Behaviour](/sdk/flutter/connection-behaviour) section. The default value for this property is **true.** +* **adminHost(adminHost: string)**: This method takes the admin URL as input and uses this admin URL instead of the default admin URL. This can be used in case of dedicated deployment of CometChat. +* **clientHost(clientHost: string)**: This method takes the client URL as input and uses this client URL instead of the default client URL. This can be used in case of dedicated deployment of CometChat. ```dart -String region = "APP_REGION"; +String region = "REGION"; String appId = "APP_ID"; AppSettings appSettings= (AppSettingsBuilder() @@ -146,101 +138,35 @@ CometChat.init(appId, appSettings, } ); ``` - - - -Replace `APP_ID` and `APP_REGION` with your credentials from the [Dashboard](https://app.cometchat.com). - - -`CometChat.init()` must be called before any other SDK method. Calling `login()`, `sendMessage()`, or registering listeners before `init()` will fail. - - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `appId` | `String` | Your CometChat App ID | -| `appSettings` | `AppSettings` | Configuration object built with `AppSettingsBuilder` | -| `onSuccess` | `Function(String)` | Callback triggered on successful initialization | -| `onError` | `Function(CometChatException)` | Callback triggered on initialization failure | - -### AppSettings Options - -| Property | Type | Description | Default | -|----------|------|-------------|---------| -| `subscriptionType` | `String?` | Presence subscription type (`CometChatSubscriptionType.allUsers`, `.roles`, `.friends`) | — | -| `roles` | `List?` | Roles to subscribe to when using role-based presence | — | -| `region` | `String?` | Region where your app was created (`us`, `eu`, `in`) | Required | -| `autoEstablishSocketConnection` | `bool` | Let SDK manage WebSocket connections automatically | `true` | -| `adminHost` | `String?` | Custom admin URL (dedicated deployment) | — | -| `clientHost` | `String?` | Custom client URL (dedicated deployment) | — | -### Presence Subscription - -Choose how to subscribe to user presence (online/offline status): - -```dart -// All users -AppSettings appSettings = (AppSettingsBuilder() - ..subscriptionType = CometChatSubscriptionType.allUsers - ..region = region -).build(); - -// Specific roles -AppSettings appSettings = (AppSettingsBuilder() - ..subscriptionType = CometChatSubscriptionType.roles - ..roles = ["admin", "moderator"] - ..region = region -).build(); - -// Friends only -AppSettings appSettings = (AppSettingsBuilder() - ..subscriptionType = CometChatSubscriptionType.friends - ..region = region -).build(); -``` + -See [User Presence](/sdk/flutter/user-presence) for more details. + - -**On Success** — A `String` message confirming SDK initialization: +We suggest you call the `init()` method on app startup. -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | `String` | Success confirmation message | `"Initialization completed successfully"` | - +## Auto Mode Connection - + -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | `String` | Human-readable error message | `"SDK initialization failed."` | -| `details` | `String` | Additional technical details | `"Please verify your App ID and region, then try again."` | - +Know more about auto mode connection [click here](/sdk/flutter/connection-behaviour) -### WebSocket Connection + -By default, the SDK manages WebSocket connections automatically. To manage them manually: +| App State | Behaviour | +| ----------------- | --------------------------------------- | +| App in foreground | Connected with WebSocket | +| App in background | Immediately disconnected with WebSocket | -```dart -AppSettings appSettings = (AppSettingsBuilder() - ..region = region - ..autoEstablishSocketConnection = false -).build(); -``` +## Manual Mode Connection -See [Connection Behaviour](/sdk/flutter/connection-behaviour) for manual control. + ---- +Know more about manual mode connection [click here](/sdk/flutter/connection-behaviour) -## Next Steps + - - - Log in users with Auth Key or Auth Token - - - Send your first message - - +| App State | Behaviour | +| ----------------- | ------------------------------------------------------------------------------------------------------------------ | +| App in foreground | Call `CometChat.connect()` to create the WebSocket connection | +| App in background | Disconnect the WebSocket connection if no ping is received within 30 seconds after the app goes in the background. | diff --git a/sdk/flutter/threaded-messages.mdx b/sdk/flutter/threaded-messages.mdx index d2b1e40f9..330506158 100644 --- a/sdk/flutter/threaded-messages.mdx +++ b/sdk/flutter/threaded-messages.mdx @@ -1,51 +1,11 @@ --- title: "Threaded Messages" -sidebarTitle: "Threaded Messages" -description: "Send, receive, and fetch threaded messages using the CometChat Flutter SDK." --- - -| Field | Value | -| --- | --- | -| Key Classes | `TextMessage`, `MediaMessage`, `CustomMessage`, `MessagesRequest`, `MessagesRequestBuilder` | -| Key Methods | `CometChat.sendMessage()`, `CometChat.sendMediaMessage()`, `CometChat.sendCustomMessage()`, `MessagesRequest.fetchPrevious()` | -| Key Properties | `parentMessageId`, `hideReplies` | -| Listener Events | `onTextMessageReceived`, `onMediaMessageReceived`, `onCustomMessageReceived` | -| Prerequisites | SDK initialized, user logged in | -```dart -// Send a message in a thread (attach to parent message) -TextMessage textMessage = TextMessage( - text: "Reply in thread", - receiverUid: "UID", - receiverType: CometChatReceiverType.user, - type: CometChatMessageType.text, -); -textMessage.parentMessageId = 103; // Parent message ID -CometChat.sendMessage(textMessage, onSuccess: (msg) {}, onError: (e) {}); - -// Fetch messages from a thread -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = "UID" - ..parentMessageId = 103 - ..limit = 50).build(); -messageRequest.fetchPrevious(onSuccess: (List list) {}, onError: (e) {}); - -// Exclude threaded messages from main conversation -MessagesRequest request = (MessagesRequestBuilder() - ..uid = "UID" - ..hideReplies = true - ..limit = 50).build(); -``` - - - -Threaded messages (or threads) are messages started from a particular parent message. Each thread is attached to a parent message. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +Messages that are started from a particular message are called Threaded messages or simply threads. +Each Thread is attached to a message which is the Parent message for that thread. ## Send Message in a Thread @@ -55,14 +15,17 @@ A message can be categorized as: 1. Text Message 2. Media Message -3. Custom Message +3. Custom Message. 4. Interactive Message -Set the `parentMessageId` on the message object to indicate which thread the message belongs to. The id specified in the `parentMessageId` parameter maps the message sent to the particular thread. Any message type — [`TextMessage`](/sdk/reference/messages#textmessage), [`MediaMessage`](/sdk/reference/messages#mediamessage), [`CustomMessage`](/sdk/reference/messages#custommessage), or Interactive Message — can be sent in a thread. +Any of the above messages can be sent in a thread. As a thread is identified with a parent message, the `parentMessageId` must be set for the message. This will indicate that the message to be sent has to be a part of the thread of the message with the specified `parentMessageId`. + +This can be achieved using the `parentMessageId` parameter provided by the object of the `TextMessage`, `MediaMessage` and `CustomMessage` class. The id specified in the `parentMessageId` parameter maps the message sent to the particular thread. +**Example to Send a Text Message in a thread in a user conversation.** - + ```dart String receiverID = "UID"; String messagesText = "Hello"; @@ -74,159 +37,27 @@ TextMessage textMessage = TextMessage( receiverUid: receiverID, receiverType: receiverType, type: type); -textMessage.parentMessageId = 103; - -CometChat.sendMessage(textMessage, onSuccess: (TextMessage message) { - debugPrint("Message sent successfully: $message"); -}, onError: (CometChatException e) { - debugPrint("Message sending failed with exception: ${e.message}"); -}); -``` - - - - -```dart -String receiverID = "GUID"; -String messagesText = "Hello"; -String receiverType = CometChatConversationType.group; -String type = CometChatMessageType.text; - -TextMessage textMessage = TextMessage( - text: messagesText, - receiverUid: receiverID, - receiverType: receiverType, - type: type); -textMessage.parentMessageId = 103; +textMessage.parentMessageId = 103 -CometChat.sendMessage(textMessage, onSuccess: (TextMessage message) { - debugPrint("Message sent successfully: $message"); +CometChat.sendMessage(textMessage,onSuccess: (TextMessage message) { +debugPrint("Message sent successfully: $message"); }, onError: (CometChatException e) { - debugPrint("Message sending failed with exception: ${e.message}"); +debugPrint("Message sending failed with exception: ${e.message}"); }); ``` -`TextMessage` Parameters: - -| Parameter | Type | Description | Required | -| --- | --- | --- | --- | -| `text` | `String` | The text content of the message | Yes | -| `receiverUid` | `String` | The `UID` of the user or `GUID` of the group to send the message to | Yes | -| `receiverType` | `String` | The type of the receiver — `CometChatConversationType.user` or `CometChatConversationType.group` | Yes | -| `type` | `String` | The type of the message — `CometChatMessageType.text` | Yes | -| `parentMessageId` | `int` | The ID of the parent message to send this message as a thread reply | Yes (for threads) | - - -**On Success** — A `TextMessage` object containing all details of the sent threaded message: - - - -**TextMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `601` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#send-thread-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#send-thread-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `103` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `text` | string | The text content of the message | `"Hello"` | -| `tags` | array | List of tags associated with the message | `[]` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `mentionedUsers` | array | List of mentioned users | `[]` | -| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | -| `reactions` | array | List of reactions on the message | `[]` | -| `moderationStatus` | string | Moderation status of the message | `null` | -| `quotedMessageId` | number | ID of the quoted message | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | -| `name` | string | Display name of the sender | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | -| `name` | string | Display name of the receiver | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to send the message."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - The above snippet shows how a message with the text "Hello" can be sent in the thread with `parentMessageId` 103. Similarly, using the `parentMessageId` parameter, Media and Custom Messages can be sent in threads too. ### Receiving Real-Time Messages -The procedure to receive real-time messages is exactly the same as mentioned in the [Receive Messages](receive-messages) section. Use the `MessageListener` class to listen for incoming thread messages. Check if the received message belongs to the active thread using the `parentMessageId` field. - - -Always remove message listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. - -```dart -CometChat.removeMessageListener("listenerId"); -``` - +The procedure to receive real-time messages is exactly the same as mentioned in the [Receive Messages](receive-messages). This can be achieved using the `MessageListener` class provided by the SDK. +To add a MessageListener, you can use the `addMessageListener()` method of the SDK. +The only thing that needs to be checked is if the received message belongs to the active thread. This can be done using `parentMessageId` field of the message object. @@ -263,17 +94,14 @@ void onCustomMessageReceived(CustomMessage customMessage) { ### Fetch all the messages for any particular thread. -Use `MessagesRequestBuilder` with `parentMessageId` to fetch messages belonging to a specific thread. Call `fetchPrevious()` to get messages (max 100 per request), returned as [`BaseMessage`](/sdk/reference/messages#basemessage) objects. Call `fetchPrevious()` again on the same object to get the next set. +You can fetch all the messages belonging to a particular thread by using the `MessagesRequest` class. +The `MessageRequestBuilder` builds the `MessageRequest` object using the following functions: -`MessagesRequestBuilder` Parameters: +1. parentMessageId: Takes the parentId of the message as argument whose thread needs to be requested. +2. build(): returns the MessageRequest object. -| Parameter | Type | Description | Required | -| --- | --- | --- | --- | -| `uid` | `String` | The `UID` of the user whose conversation thread messages are to be fetched | Yes (for user threads) | -| `guid` | `String` | The `GUID` of the group whose conversation thread messages are to be fetched | Yes (for group threads) | -| `parentMessageId` | `int` | The ID of the parent message whose thread messages are to be fetched | Yes | -| `limit` | `int` | Number of messages to fetch in a single request (max 100) | No | +Once you have the `MessagesRequest` object, you can call the `fetchPrevious()` method to get the latest messages in the thread. In one iteration, a maximum of 100 messages can be fetched. If you wish to fetch the next set of messages, you need to call the fetchPrevious() method again on the same object. @@ -295,100 +123,12 @@ messageRequest.fetchPrevious(onSuccess: (List list) { - -**On Success** — A `List` containing the fetched thread messages (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `602` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-thread-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-thread-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `103` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - ## Avoid Threaded Messages in User/Group Conversations -While fetching messages for normal user/group conversations using the `MessagesRequest`, the threaded messages by default will be a part of the list of messages received. Use `hideReplies = true` on the `MessagesRequestBuilder` to exclude threaded messages from the list. +While fetching messages for normal user/group conversations using the `MessagesRequest`, the threaded messages by default will be a part of the list of messages received. In order to exclude the threaded messages from the list of user/group messages, you need to use the `hideReplies` parameter of the `MessagesRequestBuilder` class. This method takes a boolean argument which when set to true excludes the messages belonging to threads from the list of messages. - + ```dart String UID = "cometchat-uid-1"; @@ -404,133 +144,7 @@ messageRequest.fetchNext(onSuccess: (List list) { }); ``` - - - -```dart -String GUID = "cometchat-guid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..guid = GUID - ..hideReplies = true - ..limit = 50).build(); - -messageRequest.fetchNext(onSuccess: (List list) { - debugPrint("Message fetching Successful"); - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - -**On Success** — A `List` containing the fetched messages excluding threaded replies (each item is a BaseMessage object): - - - -**BaseMessage Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `id` | number | Unique message ID | `603` | -| `metadata` | object | Custom metadata attached to the message | `{}` | -| `receiver` | object | Receiver user object | [See below ↓](#fetch-exclude-thread-receiver-object) | -| `editedBy` | string | UID of the user who edited the message | `null` | -| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | -| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | -| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | -| `type` | string | Type of the message | `"text"` | -| `readAt` | number | Epoch timestamp when the message was read | `0` | -| `deletedBy` | string | UID of the user who deleted the message | `null` | -| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | -| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | -| `replyCount` | number | Number of replies to this message | `0` | -| `sender` | object | Sender user object | [See below ↓](#fetch-exclude-thread-sender-object) | -| `receiverType` | string | Type of the receiver | `"user"` | -| `editedAt` | number | Epoch timestamp when the message was edited | `0` | -| `parentMessageId` | number | ID of the parent message (for threads) | `0` | -| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | -| `category` | string | Message category | `"message"` | -| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | -| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | -| `unreadRepliesCount` | number | Count of unread replies | `0` | -| `quotedMessageId` | number | ID of the quoted message | `null` | -| `quotedMessage` | object | The quoted message object | `null` | - ---- - - - -**`sender` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | -| `name` | string | Display name of the sender | `"George Alan"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - ---- - - - -**`receiver` Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | -| `name` | string | Display name of the receiver | `"Andrew Joseph"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | User tags | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | -| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | - - - -The response is a list of `BaseMessage` objects, excluding any messages that are replies within a thread. Only top-level messages in the conversation are returned. - ---- - -## Next Steps - - - - Learn how to send text, media, and custom messages - - - Handle incoming messages in real-time - - - Add emoji reactions to messages - - - Understand message types and hierarchy - - +The above snippet will return messages between the logged in user and `cometchat-uid-1` excluding all the threaded messages belonging to the same conversation. \ No newline at end of file diff --git a/sdk/flutter/transfer-group-ownership.mdx b/sdk/flutter/transfer-group-ownership.mdx index d5c67b622..5c2b8b668 100644 --- a/sdk/flutter/transfer-group-ownership.mdx +++ b/sdk/flutter/transfer-group-ownership.mdx @@ -1,33 +1,14 @@ --- title: "Transfer Group Ownership" -sidebarTitle: "Transfer Ownership" -description: "Transfer ownership of a CometChat group to another member using the Flutter SDK." --- - -```dart -// Transfer group ownership to another member -CometChat.transferGroupOwnership( - guid: "GROUP_ID", - uid: "NEW_OWNER_UID", - onSuccess: (String message) => debugPrint("Ownership transferred: $message"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); -``` - -**Note:** Only the current group owner can transfer ownership. The owner must transfer ownership before leaving the group. - -Transfer ownership of a group to another member. Only the current owner can do this, and since owners cannot leave their group, you must transfer ownership first if you want to leave. The group is represented by a [`Group`](/sdk/reference/entities#group) object and the new owner by a [`User`](/sdk/reference/entities#user) object. See [Leave Group](/sdk/flutter/leave-group). +*In other words, as a logged-in user, how do I transfer the ownership of any group if I am the owner of the group?* -## Transfer Ownership +In order to transfer the ownership of any group, the first condition is that you must be the owner of the group. In case you are the owner of the group, you can use the `transferGroupOwnership()` method provided by the `CometChat` class. -Use `transferGroupOwnership()` to transfer ownership to another group member. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +This will be helpful as the owner is not allowed to leave the group. In case, you as the owner would like to leave the group, you will have to use this method and transfer your ownership first to any other member of the group and only then you will be allowed to leave the group. @@ -46,50 +27,3 @@ CometChat.transferGroupOwnership(guid: GUID,uid: UID, - -This method takes the below parameters: - -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `guid` | `String` | The GUID of the group for which ownership needs to be transferred | -| `uid` | `String` | The UID of the member who should become the new owner | -| `onSuccess` | `Function(String)` | Callback triggered on successful ownership transfer | -| `onError` | `Function(CometChatException)` | Callback triggered on error | - -On success, the new owner gets admin scope and the previous owner becomes a participant. - - -**On Success** — A `String` message confirming the ownership transfer: - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `message` | string | Success confirmation message | `"cometchat-guid-1 ownership transferred successfully"` | - - - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_NOT_A_MEMBER"` | -| `message` | string | Human-readable error message | `"The target user is not a member of the group."` | -| `details` | string | Additional technical details | `"Ownership can only be transferred to an existing group member."` | - - - ---- - -## Next Steps - - - - Leave a group after transferring ownership - - - Promote or demote group members - - diff --git a/sdk/flutter/transient-messages.mdx b/sdk/flutter/transient-messages.mdx index 9b7654664..e39b5e46f 100644 --- a/sdk/flutter/transient-messages.mdx +++ b/sdk/flutter/transient-messages.mdx @@ -1,90 +1,28 @@ --- title: "Transient Messages" -sidebarTitle: "Transient Messages" -description: "Send and receive ephemeral real-time messages that are not stored on the server using the CometChat Flutter SDK. Ideal for live reactions and temporary indicators." --- - -| Field | Value | -| --- | --- | -| Key Classes | `TransientMessage` | -| Key Methods | `CometChat.sendTransientMessage()` | -| Receiver Types | `CometChatReceiverType.user`, `CometChatReceiverType.group` | -| Listener Events | `onTransientMessageReceived` | -| Prerequisites | SDK initialized, user logged in | - -```dart -// Send transient message (not stored on server) -Map data = {"LIVE_REACTION": "heart"}; -TransientMessage transientMessage = TransientMessage( - receiverId: "UID", - receiverType: CometChatReceiverType.user, - data: data, -); -CometChat.sendTransientMessage(transientMessage, - onSuccess: () => debugPrint("Sent"), - onError: (e) => debugPrint("Error: ${e.message}"), -); - -// Receive transient messages -CometChat.addMessageListener("LISTENER_ID", MessageListener( - onTransientMessageReceived: (TransientMessage message) { - debugPrint("Received: ${message.data}"); - }, -)); -``` - - Transient messages are messages that are sent in real-time only and are not saved or tracked anywhere. The receiver of the message will only receive the message if he is online and these messages cannot be retrieved later. - -**Available via:** SDK | UI Kits - - ## Send a Transient Message -You can use the `sendTransientMessage()` method to send a transient message to a user or in a group. The receiver will receive this information in the `onTransientMessageReceived()` method of the `MessageListener` class. In order to send the transient message, you need to use the [`TransientMessage`](/sdk/reference/auxiliary#transientmessage) class. +You can use the `sendTransientMessage()` method to send a transient message to a user or in a group. The receiver will receive this information in the `onTransientMessageReceived()` method of the `MessageListener` class. In order to send the transient message, you need to use the `TransientMessage` class. - + ```dart String receiverId = "cometchat-uid-2"; -Map data = {}; -data["LIVE_REACTION"] = "heart"; - -TransientMessage transientMessage = TransientMessage( - receiverId: receiverId, - receiverType: CometChatReceiverType.user, - data: data, -); - -CometChat.sendTransientMessage(transientMessage, onSuccess: () { - debugPrint("Transient Message Sent"); -}, onError: (CometChatException e) { - debugPrint("Transient message sending failed with exception: ${e.message}"); -}); -``` - - - - -```dart -String receiverId = "cometchat-guid-1"; -Map data = {}; -data["LIVE_REACTION"] = "heart"; - -TransientMessage transientMessage = TransientMessage( - receiverId: receiverId, - receiverType: CometChatReceiverType.group, - data: data, -); - -CometChat.sendTransientMessage(transientMessage, onSuccess: () { - debugPrint("Transient Message Sent"); -}, onError: (CometChatException e) { - debugPrint("Transient message sending failed with exception: ${e.message}"); +Map data= {}; +data["LIVE_REACTION"] = "heart"; + +TransientMessage transientMessage = TransientMessage( receiverId:receiverId , receiverType: CometChatReceiverType.user , data: data, ); + +CometChat.sendTransientMessage(transientMessage, onSuccess: (){ + debugPrint("Transient Message Sent"); + }, onError: (CometChatException e){ + debugPrint("Transient message sending failed with exception: ${e.message}"); }); ``` @@ -92,37 +30,10 @@ CometChat.sendTransientMessage(transientMessage, onSuccess: () { -`TransientMessage` Parameters: - -| Parameter | Type | Description | Required | -| --- | --- | --- | --- | -| `receiverId` | `String` | The `UID` of the user or `GUID` of the group to send the transient message to | Yes | -| `receiverType` | `String` | The type of the receiver — `CometChatReceiverType.user` or `CometChatReceiverType.group` | Yes | -| `data` | `Map` | A map to provide custom data with the transient message | Yes | -| `sender` | `User?` | The sender of the transient message (set automatically by the SDK) | No | - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | -| `message` | `String` | Human-readable error message | `"Failed to send the transient message."` | -| `details` | `String` | Additional technical details | `"An unexpected error occurred while sending the transient message."` | - - - ## Real-time Transient Messages *In other words, as a recipient, how do I know when someone sends a transient message?* - -Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. - -```dart -CometChat.removeMessageListener("listenerId"); -``` - - You will receive the transient message in the `onTransientMessageReceived()` method of the registered `MessageListener` class. @@ -144,24 +55,11 @@ void onTransientMessageReceived(TransientMessage message) { -The received object is a `TransientMessage` with the following fields: - -| Parameter | Type | Description | -| --- | --- | --- | -| `sender` | `User?` | An object of the `User` class holding all the information related to the sender of the transient message. | -| `receiverId` | `String` | Unique ID of the receiver. This can be the `UID` of the user or `GUID` of the group the transient message is sent to. | -| `receiverType` | `String` | The type of the receiver — `CometChatReceiverType.user` or `CometChatReceiverType.group`. | -| `data` | `Map` | A map containing the custom data sent with the transient message. | - ---- - -## Next Steps +The `TransientMessage` class consists of the below parameters: - - - Learn how to send persistent text and media messages - - - Show real-time typing status to users - - +| Parameter | Information | +| ---------------- | --------------------------------------------------------------------------------------------------------- | +| **sender** | An object of the User class holding all the information. related to the sender of the transient message. | +| **receiverId** | Unique Id of the receiver. This can be the Id of the group or the user the transient message is sent to. | +| **receiverType** | The type of the receiver - `CometChatReceiverType.user` (user) or `CometChatReceiverType.``group `(group) | +| **data** | A Map to provide data. | diff --git a/sdk/flutter/typing-indicators.mdx b/sdk/flutter/typing-indicators.mdx index 19640d53d..c3787c105 100644 --- a/sdk/flutter/typing-indicators.mdx +++ b/sdk/flutter/typing-indicators.mdx @@ -1,43 +1,8 @@ --- title: "Typing Indicators" -sidebarTitle: "Typing Indicators" -description: "Send and receive real-time typing indicators using the CometChat Flutter SDK." --- - -| Field | Value | -| --- | --- | -| Key Classes | `TypingIndicator` | -| Key Methods | `CometChat.startTyping()`, `CometChat.endTyping()` | -| Receiver Types | `CometChatReceiverType.user`, `CometChatReceiverType.group` | -| Listener Events | `onTypingStarted`, `onTypingEnded` | -| Prerequisites | SDK initialized, user logged in | - -```dart -// Start typing indicator to user -CometChat.startTyping(receiverUid: "UID", receiverType: CometChatReceiverType.user); - -// Start typing indicator to group -CometChat.startTyping(receiverUid: "GUID", receiverType: CometChatReceiverType.group); - -// Stop typing indicator -CometChat.endTyping(receiverUid: "UID", receiverType: CometChatReceiverType.user); - -// Listen for typing indicators -CometChat.addMessageListener("listenerId", MessageListener( - onTypingStarted: (TypingIndicator typingIndicator) { }, - onTypingEnded: (TypingIndicator typingIndicator) { }, -)); -``` - - - -Typing indicators let users know when someone is actively typing a message, creating a more engaging real-time chat experience. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - ## Send a Typing Indicator @@ -45,93 +10,68 @@ Typing indicators let users know when someone is actively typing a message, crea ### Start Typing -Use `startTyping()` to notify the receiver that the logged-in user has started typing. The receiver will receive this information in the `onTypingStarted()` method of the `MessageListener` class. +You can use the `startTyping()` method to inform the receiver that the logged in user has started typing. The receiver will receive this information in the `onTypingStarted()` method of the `MessageListener` class. In order to send the typing indicator, you need to use the `TypingIndicator` class. - + ```dart CometChat.startTyping( - receiverUid: "UID", - receiverType: CometChatReceiverType.user, -); + receiverUid: "UID", + receiverType: CometChatReceiverType.user, + ); ``` - + ```dart CometChat.startTyping( - receiverUid: "GUID", - receiverType: CometChatReceiverType.group, -); + receiverUid: "GUID", + receiverType: CometChatReceiverType.group, + ); ``` -`startTyping()` Parameters: - -| Parameter | Type | Description | Required | -| --- | --- | --- | --- | -| `receiverUid` | `String` | The `UID` of the user or `GUID` of the group to send the typing indicator to | Yes | -| `receiverType` | `String` | The type of the receiver — `CometChatReceiverType.user` or `CometChatReceiverType.group` | Yes | - -`startTyping()` returns `void` — the typing indicator is sent as a fire-and-forget operation. - ### Stop Typing -Use `endTyping()` to notify the receiver that the logged-in user has stopped typing. The receiver will receive this information in the `onTypingEnded()` method of the `MessageListener` class. +You can use the `endTyping()` method to inform the receiver that the logged in user has stopped typing. The receiver will receive this information in the `onTypingEnded()` method of the `MessageListener` class. In order to send the typing indicator, you need to use the `TypingIndicator` class. - + ```dart CometChat.endTyping( - receiverUid: "UID", - receiverType: CometChatReceiverType.user, -); + receiverUid: "UID", + receiverType: CometChatReceiverType.user); ``` - + ```dart CometChat.endTyping( - receiverUid: "GUID", - receiverType: CometChatReceiverType.group, -); + receiverUid: "GUID", + receiverType: CometChatReceiverType.group); ``` -`endTyping()` Parameters: - -| Parameter | Type | Description | Required | -| --- | --- | --- | --- | -| `receiverUid` | `String` | The `UID` of the user or `GUID` of the group to send the typing indicator to | Yes | -| `receiverType` | `String` | The type of the receiver — `CometChatReceiverType.user` or `CometChatReceiverType.group` | Yes | + +Custom Data -`endTyping()` returns `void` — the typing indicator is sent as a fire-and-forget operation. +You can use the `metadata` field of the `TypingIndicator` class to pass additional data along with the typing indicators. The metadata field is a Map and can be set using the `.metadata` parameter of the `TypingIndicator` class. This data will be received at the receiver end and can be obtained using the same parameter. - -Use the `metadata` field of the `TypingIndicator` class to pass additional custom data along with the typing indicators. The metadata field is a `Map` and can be set using the `.metadata` property. This data will be received at the receiver end and can be obtained using the same property. ## Real-time Typing Indicators *In other words, as a recipient, how do I know when someone is typing?* -Use `onTypingStarted` and `onTypingEnded` in `MessageListener` to receive [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) events. - - -Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. - -```dart -CometChat.removeMessageListener("listenerId"); -``` - +You will receive the typing indicators in the `onTypingStarted()` and the `onTypingEnded()` method of the registered `MessageListener` class. @@ -157,25 +97,11 @@ void onTypingEnded(TypingIndicator typingIndicator) { -The received object is a `TypingIndicator` with the following fields: - -| Parameter | Type | Description | -| --- | --- | --- | -| `sender` | [`User`](/sdk/reference/entities#user) | An object of the `User` class holding all the information related to the sender of the typing indicator. | -| `receiverId` | `String` | `UID` of the receiver. This is the ID of the group or the user the typing indicator is being sent to. | -| `receiverType` | `String` | Indicates if the typing indicator is to a user or a group — `CometChatReceiverType.user` or `CometChatReceiverType.group`. | -| `metadata` | `Map?` | Optional metadata to provide additional data. | -| `lastTimestamp` | `DateTime` | The timestamp of the last typing indicator event. | - ---- - -## Next Steps +The `TypingIndicator` class consists of the below parameters: - - - Track message delivery and read status - - - Send ephemeral real-time messages like live reactions - - +| Parameter | Information | +| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sender` | An object of the `User` class holding all the information related to the sender of the typing indicator. | +| `receiverId` | `UID` of the receiver. This is the ID of the group or the user the typing indicator is being sent to. | +| `receiverType` | This parameter indicates if the typing indicator is to be sent to a user or a group. The possible values are: 1. `CometChatConstants.RECEIVER_TYPE_USER` 2. `CometChatConstants.RECEIVER_TYPE_GROUP` | +| `metadata` | A JSONObject to provider additional data | diff --git a/sdk/flutter/update-group.mdx b/sdk/flutter/update-group.mdx index 8371e41af..c9353f983 100644 --- a/sdk/flutter/update-group.mdx +++ b/sdk/flutter/update-group.mdx @@ -1,44 +1,14 @@ --- title: "Update A Group" -sidebarTitle: "Update Group" -description: "Update group details such as name, description, icon, and metadata using the CometChat Flutter SDK." --- - -```dart -// Update a group -Group group = Group(guid: "GROUP_ID", name: "New Name", type: CometChatGroupType.public); -group.description = "Updated description"; -group.icon = "https://example.com/icon.png"; - -await CometChat.updateGroup( - group: group, - onSuccess: (Group group) => debugPrint("Updated: ${group.name}"), - onError: (CometChatException e) => debugPrint("Error: ${e.message}"), -); -``` - - -Update a group's name, icon, description, or metadata. The GUID and group type cannot be changed after creation. See the [Group Class](/sdk/flutter/create-group#group-class) reference for all editable fields. ## Update Group *In other words, as a group owner, how can I update the group details?* -You can update the existing details of the group using the `updateGroup()` method. Pass a [`Group`](/sdk/reference/entities#group) object with the updated values. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `group` | `Group` | An instance of the [`Group`](/sdk/reference/entities#group) class with updated values | -| `onSuccess` | `Function(Group group)?` | Callback triggered on successful group update | -| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | +You can update the existing details of the group using the `updateGroup()` method. @@ -62,67 +32,12 @@ String GUID = "GUID"; - -**On Success** — A `Group` object containing all details of the updated group: - - - -**Group Object:** +This method takes an instance of the `Group` class as a parameter which should contain the data that you wish to update. -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | -| `name` | string | Display name of the group | `"Hello Group!"` | -| `icon` | string | URL of the group icon | `null` | -| `description` | string | Description of the group | `null` | -| `membersCount` | number | Number of members in the group | `5` | -| `metadata` | object | Custom metadata attached to the group | `{}` | -| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | -| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | -| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | -| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | -| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554800` | -| `tags` | array | List of tags associated with the group | `[]` | -| `type` | string | Type of the group (public, private, password) | `"public"` | -| `scope` | string | Scope of the logged-in user in the group | `"admin"` | -| `password` | string | Password for password-protected groups | `null` | -| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | - - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified group does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | - - +| Parameter | Description | +| --------- | ---------------------------- | +| `group` | an instance of class `Group` | After the successful update of the group, you will receive an instance of `Group` class containing updated information of the group. -For more information on the `Group` class, please check [here](/sdk/flutter/create-group#group-class). - - -There is no real-time event listener for group updates. To get the latest group information after calling `updateGroup()`, fetch the group details again using `getGroup()`. - - ---- - -## Next Steps - - - - Permanently delete a group - - - Fetch and filter groups with pagination - - - Create new public, private, or password-protected groups - - - Overview of all group management features - - +For more information on the `Group` class, please check [here](/sdk/flutter/create-group#group-class) diff --git a/sdk/flutter/v5/upgrading-from-v4-guide.mdx b/sdk/flutter/upgrading-from-v4-guide.mdx similarity index 94% rename from sdk/flutter/v5/upgrading-from-v4-guide.mdx rename to sdk/flutter/upgrading-from-v4-guide.mdx index 5347d2bd4..04728b860 100644 --- a/sdk/flutter/v5/upgrading-from-v4-guide.mdx +++ b/sdk/flutter/upgrading-from-v4-guide.mdx @@ -6,16 +6,16 @@ This guide helps you migrate your Flutter application from CometChat SDK v4 to v ## Installation -### Cloudsmith (Current Beta) +### Cloudsmith -For the current beta release, add the Cloudsmith hosted repository and dependency to your `pubspec.yaml`: +Add the Cloudsmith hosted repository and dependency to your `pubspec.yaml`: ```yaml dependencies: cometchat_sdk: hosted: url: https://dart.cloudsmith.io/cometchat/cometchat/ - version: 5.0.0-beta.1 + version: 5.0.0 ``` Then run: diff --git a/sdk/flutter/user-management.mdx b/sdk/flutter/user-management.mdx index 95622f92c..b58d3da36 100644 --- a/sdk/flutter/user-management.mdx +++ b/sdk/flutter/user-management.mdx @@ -1,251 +1,88 @@ --- title: "User Management" -sidebarTitle: "User Management" -description: "Create, update, and manage CometChat users programmatically using the Flutter SDK. Includes user creation, profile updates, and the User class reference." --- - -```dart -// Create a user -User user = User(uid: "user1", name: "Kevin"); -CometChat.createUser(user, "AUTH_KEY", onSuccess: (User user) { - debugPrint("User created: ${user.name}"); -}, onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); -}); - -// Update a user (requires API Key) -User updatedUser = User(uid: "user1", name: "Kevin Fernandez"); -CometChat.updateUser(updatedUser, "AUTH_KEY", onSuccess: (User user) { - debugPrint("User updated: ${user.name}"); -}, onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); -}); - -// Update logged-in user (no auth key needed) -User currentUser = User(name: "New Name"); -CometChat.updateCurrentUserDetails(currentUser, onSuccess: (User user) { - debugPrint("User updated: ${user.name}"); -}, onError: (CometChatException e) { - debugPrint("Error: ${e.message}"); -}); -``` -**Note:** User creation/deletion should ideally happen on your backend via the [REST API](https://api-explorer.cometchat.com). - +When a user logs into your app, you need to programmatically login the user into CometChat. But before you log in the user to CometChat, you need to create the user. -Users must exist in CometChat before they can log in. This page covers creating, updating, and deleting users. All methods that return user data return a [`User`](/sdk/reference/entities#user) object. +Summing up- -The typical flow: -1. User registers in your app → Create user in CometChat -2. User logs into your app → [Log user into CometChat](/sdk/flutter/authentication-overview) +**When a user registers in your app** - -User deletion is only available via the [REST API](https://api-explorer.cometchat.com/reference/delete-user) — there is no client-side SDK method for it. - +1. You add the user details in your database +2. You create a user in CometChat -## Creating a User +**When a user logs into your app** -User creation should ideally happen on your backend via the [REST API](https://api-explorer.cometchat.com/reference/creates-user). +1. You log in the user to your app +2. You [log in the user to CometChat](https://app.cometchat.com/login) - -**Security:** Never expose your `Auth Key` in client-side production code. User creation and updates using `Auth Key` should ideally happen on your backend server. Use client-side creation only for prototyping or development. - +## Creating a user + +Ideally, user creation should take place at your backend. You can refer our Rest API to learn more about [creating a user](https://api-explorer.cometchat.com/reference/create-user) and use the appropriate code sample based on your backend language. -For client-side creation (development only), use `createUser()`: +However, if you wish to create users on the fly, you can use the `createUser()` method. This method takes a `User` object and the `API Key` as input parameters and returns the created `User` object if the request is successful. + +For more details on the fields present in the User class, please check [this](/sdk/flutter/user-management#user-class) ```dart -String authKey = "AUTH_KEY"; // Replace with the auth key of app -User user = User(uid: "usr1", name: "Kevin"); // Replace with name and uid of user - -CometChat.createUser(user, authKey, onSuccess: (User user) { - debugPrint("Create User successful $user"); -}, onError: (CometChatException e) { - debugPrint("Create User Failed with exception ${e.message}"); -}); -``` - - - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `user` | `User` | A `User` object containing the details of the user to be created. The `uid` and `name` fields are **required**. | -| `authKey` | `String` | Your CometChat Auth Key. Use only for development — never expose in production client code. | -| `onSuccess` | `Function(User user)` | Callback triggered on successful user creation, returning the created `User` object. | -| `onError` | `Function(CometChatException excep)` | Callback triggered on failure, returning a `CometChatException` with error details. | - -Returns a [`User`](/sdk/reference/entities#user) object. See [User Class](#user-class) for all available fields. - - -**On Success** — A `User` object containing all details of the created user: - - - -**User Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"usr1"` | -| `name` | string | Display name of the user | `"Kevin"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `null` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | List of tags associated with the user | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `0` | - - - - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified UID does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid UID for the user."` | - +String authKey = "AUTH_KEY";//Replace with the auth key of app +User user = User( uid: "usr1" , name: "Kevin" );//Replace with name and uid of user - - -UID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. - - +CometChat.createUser(user, authKey, onSuccess: (User user){ + debugPrint("Create User succesful ${user}"); -## Updating a User + }, onError: (CometChatException e){ + debugPrint("Create User Failed with exception ${e.message}"); -Like creation, user updates should ideally happen on your backend via the [REST API](https://api-explorer.cometchat.com/reference/update-user). - -For client-side updates (development only), use `updateUser()`: - - - -```dart -String apiKey = "AUTH_KEY"; // Replace with the auth key of app -User user = User(uid: "usr1", name: "Kevin Fernandez"); - -CometChat.updateUser(user, apiKey, onSuccess: (User user) { - debugPrint("User updated: $user"); -}, onError: (CometChatException e) { - debugPrint("Update User Failed with exception ${e.message}"); -}); + }); ``` - - -Ensure the [`User`](/sdk/reference/entities#user) object has the correct `UID` set. + -### Parameters + -| Parameter | Type | Description | -|-----------|------|-------------| -| `user` | `User` | A `User` object with the `uid` of the user to update and the fields to change. | -| `apiKey` | `String` | Your CometChat Auth Key. Use only for development — never expose in production client code. | -| `onSuccess` | `Function(User retUser)` | Callback triggered on successful update, returning the updated `User` object. | -| `onError` | `Function(CometChatException excep)` | Callback triggered on failure, returning a `CometChatException` with error details. | + -Returns a [`User`](/sdk/reference/entities#user) object. See [User Class](#user-class) for all available fields. +UID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. - -**On Success** — A `User` object containing all details of the updated user: + -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"usr1"` | -| `name` | string | Display name of the user | `"Kevin Fernandez"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `null` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"offline"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | List of tags associated with the user | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `0` | - +## Updating a user - +Updating a user similar to creating a user should ideally be achieved at your backend using the Restful APIs. For more information, you can check the [update a user](https://api-explorer.cometchat.com/reference/update-user) section. However, this can be achieved on the fly as well using the `updateUser()` method. This method takes a `User` object and the API Key as inputs and returns the updated `User` object on successful execution of the request. -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified UID does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid UID for the user."` | - +Please make sure the `User` object provided to the `updateUser()` method has the `UID` of the user to be updated set. -## Updating Logged-in User +## Updating logged-in user -Use `updateCurrentUserDetails()` to update the current user without an Auth Key. Note: You cannot update the user's role with this method. +Updating a logged-in user is similar to updating a user. The only difference being this method does not require an AuthKey. This method takes a `User` object as input and returns the updated `User` object on the successful execution of the request. ```dart User user = User(name: 'Updated Name'); + CometChat.updateCurrentUserDetails(user, onSuccess: (User updatedUser) { - debugPrint("Updated User: $updatedUser"); -}, onError: (CometChatException e) { - debugPrint("Updated User exception : ${e.message}"); -}); + debugPrint("Updated User: $updatedUser"); + }, onError: (CometChatException e) { + debugPrint("Updated User exception : ${e.message}"); + }); ``` - - -### Parameters - -| Parameter | Type | Description | -|-----------|------|-------------| -| `user` | `User` | A `User` object with the fields to update. The `uid` is ignored — only the logged-in user is updated. | -| `onSuccess` | `Function(User retUser)` | Callback triggered on successful update, returning the updated `User` object. | -| `onError` | `Function(CometChatException excep)` | Callback triggered on failure, returning a `CometChatException` with error details. | - -The method returns a [`User`](/sdk/reference/entities#user) object. - - -**On Success** — A `User` object containing all details of the updated user: - - - -**User Object:** - -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | -| `name` | string | Display name of the user | `"Updated Name"` | -| `link` | string | Profile link | `null` | -| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | -| `metadata` | object | Custom metadata | `{}` | -| `status` | string | Online status | `"online"` | -| `role` | string | User role | `"default"` | -| `statusMessage` | string | Status message | `null` | -| `tags` | array | List of tags associated with the user | `[]` | -| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | -| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | -| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | - - - + -| Parameter | Type | Description | Sample Value | -|-----------|------|-------------|--------------| -| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | -| `message` | string | Human-readable error message | `"The specified UID does not exist."` | -| `details` | string | Additional technical details | `"Please provide a valid UID for the user."` | - + By using the `updateCurrentUserDetails()` method one can only update the logged-in user irrespective of the UID passed. Also, it is not possible to update the role of a logged-in user. -## Deleting a User +## Deleting a user -User deletion is only available via the [REST API](https://api-explorer.cometchat.com/reference/delete-user). +Deleting a user can only be achieved via the Restful APIs. For more information please check the [delete a user](https://api-explorer.cometchat.com/reference/delete-user)section. ## User Class @@ -263,20 +100,3 @@ User deletion is only available via the [REST API](https://api-explorer.cometcha | hasBlockedMe | No | A boolean that determines if the user has blocked the logged in user | | blockedByMe | No | A boolean that determines if the logged in user has blocked the user | | tags | Yes | A list of tags to identify specific users | - -## Next Steps - - - - Fetch and filter user lists with pagination. - - - Monitor real-time online/offline status. - - - Block and unblock users. - - - Log users into CometChat. - - diff --git a/sdk/flutter/user-presence.mdx b/sdk/flutter/user-presence.mdx index c1f0fecea..a9721a3d9 100644 --- a/sdk/flutter/user-presence.mdx +++ b/sdk/flutter/user-presence.mdx @@ -1,62 +1,30 @@ --- title: "User Presence" -sidebarTitle: "User Presence" -description: "Track when users come online or go offline in real-time using CometChat's presence subscription system." --- -{/* TL;DR for Agents and Quick Reference */} - -```dart -// Configure presence subscription during init -AppSettings appSettings = (AppSettingsBuilder() - ..subscriptionType = CometChatSubscriptionType.allUsers // or .roles, .friends - ..region = "REGION" - ..autoEstablishSocketConnection = true -).build(); - -await CometChat.init("APP_ID", appSettings, onSuccess: (msg) {}, onError: (e) {}); - -// Listen for presence changes -CometChat.addUserListener("UNIQUE_LISTENER_ID", UserListener( - onUserOnline: (User user) { - debugPrint("${user.name} is online"); - }, - onUserOffline: (User user) { - debugPrint("${user.name} is offline"); - }, -)); - -// Remove listener when done -CometChat.removeUserListener("UNIQUE_LISTENER_ID"); -``` - -Track whether users are online or offline in real-time. - - -**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) - +User Presence helps us understand if a user is available to chat or not. ## Real-time Presence *In other words, as a logged-in user, how do I know if a user is online or offline?* -Configure presence subscription in `AppSettings` during SDK initialization. The `AppSettingsBuilder` provides three subscription options: +Based on the settings provided in the `AppSettings` class while initializing CometChat using the `init()` method, the logged-in user will receive the presence for the other users in the app. + +In the `AppSettings` class, you can set the type of presence you wish to receive. -| Method | Description | -| ------ | ----------- | -| `subscribePresenceForAllUsers()` | Receive presence updates for all users | -| `subscribePresenceForRoles(List roles)` | Receive presence updates only for users with specified roles | -| `subscribePresenceForFriends()` | Receive presence updates only for friends | +For presence subscription, the `AppSettingsBuilder` provides 3 methods : -If none of these methods are called, no presence events will be delivered. +* `subscribePresenceForAllUsers()` - This will inform the logged-in user when any user in the app comes online or goes offline. +* `subscribePresenceForRoles(List roles)` - This will inform the logged-in user, only when the users with the specified roles come online or go offline. +* `subscribePresenceForFriends()` - This will inform the logged-in user when any of their friends come online or go offline. - -You must configure presence subscription in `AppSettings` during `CometChat.init()` before any presence events will be delivered. See [Setup SDK](/sdk/flutter/setup) for details. - +If none of the above methods are set, no presence will be sent to the logged-in user. -Register a `UserListener` to receive presence events. We suggest adding the listener in the `init` method of the activity or the fragment where you wish to receive these events in. +For every activity or fragment you wish to receive user events in, you need to register the `UserListener` using the `addUserListener()` method. + +We suggest adding the listener in the `init` method of the activity or the fragment where you wish to receive these events in. @@ -85,23 +53,9 @@ void onUserOffline(User user) { | ------------ | ----------------------------------------------------------------------------------------------- | | `listenerID` | An ID that uniquely identifies that listener. We recommend using the activity or fragment name. | -### UserListener Events - -| Event | Description | -| ----- | ----------- | -| `onUserOnline(User user)` | Triggered when a subscribed user comes online | -| `onUserOffline(User user)` | Triggered when a subscribed user goes offline | - -Each callback receives a [`User`](/sdk/reference/entities#user) object with presence information. +You will receive an object of the `User` class in the listener methods. -Relevant fields to access on returned users: - -| Field | Type | Description | -|-------|------|-------------| -| `status` | `String` | Online status of the user (`"online"` or `"offline"`) | -| `lastActiveAt` | `int` | Timestamp when the user was last active | - -Remove the listener when no longer needed: +We recommend you remove the listener once the listener is not in use. @@ -115,15 +69,11 @@ CometChat.removeUserListener(listenerID); - -Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. - - ## User List Presence *In other words, as a logged-in user, when I retrieve the user list, how do I know if a user is online/offline?* -When you [retrieve the list of users](/sdk/flutter/retrieve-users), in the [User](/sdk/flutter/user-management) object, you will receive 2 keys: +When you [retrieve the list of users](/sdk/flutter/retrieve-users) , in the[User](/sdk/flutter/user-management#user-class) object, you will receive 2 keys: 1. `status` - This will hold either of the two values : @@ -131,22 +81,3 @@ When you [retrieve the list of users](/sdk/flutter/retrieve-users), in the [User * `offline` - This indicates that the user is currently offline and is not available to chat. 2. `lastActiveAt` - In case the user is offline, this field holds the timestamp of the time when the user was last online. This can be used to display a **Last seen** for that user. - ---- - -## Next Steps - - - - Fetch user lists with filtering and pagination - - - Create and update users programmatically - - - Monitor SDK connection to CometChat servers - - - Block and unblock users in your app - - diff --git a/sdk/flutter/users-overview.mdx b/sdk/flutter/users-overview.mdx index 32e83bbb2..8dcfcd91e 100644 --- a/sdk/flutter/users-overview.mdx +++ b/sdk/flutter/users-overview.mdx @@ -1,37 +1,35 @@ --- title: "Users" sidebarTitle: "Overview" -description: "Overview of CometChat user functionality including user management, retrieval, and presence tracking in the Flutter SDK." --- - +A user is anyone who uses CometChat. The primary aim for the users functionality is to allow you to quickly retrieve and add users to CometChat. -- [User Management](/sdk/flutter/user-management) — Create and update users -- [Retrieve Users](/sdk/flutter/retrieve-users) — Fetch and filter user lists -- [User Presence](/sdk/flutter/user-presence) — Track online/offline status -- [Block Users](/sdk/flutter/block-users) — Block and unblock users - +## UID -Every person who uses your app needs a corresponding user in CometChat. Once a user exists, you can manage their profile, fetch user lists for your UI, track who's online, and control communication with blocking. +- Each user is uniquely identified using a UID. +- The UID is typically the primary ID of the user from your database. -- [User Management](/sdk/flutter/user-management) — Create users when they sign up, update profiles, and delete accounts -- [Retrieve Users](/sdk/flutter/retrieve-users) — Fetch and filter user lists with pagination, search, and role-based filtering -- [User Presence](/sdk/flutter/user-presence) — Monitor real-time online/offline status and subscribe to presence changes -- [Block Users](/sdk/flutter/block-users) — Block and unblock users to prevent all communication + +UID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. + + +## Auth Token + +- A single user can have multiple auth tokens. Auth tokens should be per user per device. +- It should be generated via a server-to-server API call. The auth token should then be passed to CometChat for login. +- An Auth Token can only be deleted via the dashboard or using the REST API. + +## User Roles + +A role is a category for a group of similar users. For example, you may want to group your premium users using the role "Premium". You then use this to filter users or enable/disable features by writing conditional code. + +## User List + +- The User List can be used to build the **Contacts** or **Who's Online** view in your app. +- The list of users can be different based on the logged-in user. ## Next Steps - - - Create, update, and delete users in CometChat. - - - Fetch user lists with filtering, sorting, and pagination. - - - Monitor real-time online/offline status of users. - - - Block and unblock users to control communication. - - +- [User Management](/sdk/flutter/v5/user-management) — sync your users to CometChat +- [Retrieve Users](/sdk/flutter/v5/retrieve-users) — fetch and display users in your app diff --git a/sdk/flutter/v4/additional-message-filtering.mdx b/sdk/flutter/v4/additional-message-filtering.mdx new file mode 100644 index 000000000..35f03b472 --- /dev/null +++ b/sdk/flutter/v4/additional-message-filtering.mdx @@ -0,0 +1,660 @@ +--- +title: "Additional Message Filtering" +sidebarTitle: "Message Filtering" +description: "Advanced filtering options for fetching messages using MessagesRequestBuilder in the CometChat Flutter SDK." +--- + + + +```dart +// Basic message request for user conversation +MessagesRequest request = (MessagesRequestBuilder() + ..uid = "user_uid" + ..limit = 30 +).build(); + +// Fetch messages for group with filters +MessagesRequest request = (MessagesRequestBuilder() + ..guid = "group_guid" + ..limit = 50 + ..categories = ["message", "custom"] + ..types = ["text", "image"] + ..hideReplies = true +).build(); + +// Unread messages only +MessagesRequest request = (MessagesRequestBuilder() + ..uid = "user_uid" + ..unread = true + ..limit = 50 +).build(); + +// Paginate through messages +List messages = await request.fetchPrevious(); +List moreMessages = await request.fetchPrevious(); // Next page +``` + +**Key properties:** `uid`, `guid`, `limit`, `categories`, `types`, `tags`, `unread`, `parentMessageId`, `messageId`, `timestamp`, `hideReplies`, `hideDeleted`, `hideQuotedMessages`, `searchKeyword`, `updatedAfter`, `updatesOnly`, `hideMessagesFromBlockedUsers`, `withTags`, `hasMentions`, `hasLinks`, `hasAttachments`, `hasReactions`, `mentionedUids`, `attachmentTypes`, `withParent` + + +The `MessagesRequest` class helps you fetch messages based on various parameters — returning [`BaseMessage`](/sdk/reference/messages#basemessage) objects that can be [`TextMessage`](/sdk/reference/messages#textmessage), [`MediaMessage`](/sdk/reference/messages#mediamessage), [`CustomMessage`](/sdk/reference/messages#custommessage), [`Action`](/sdk/reference/messages#action), or [`Call`](/sdk/reference/messages#call). It uses the Builder design pattern via `MessagesRequestBuilder`. + +To fetch messages: +1. Create a `MessagesRequestBuilder` object +2. Set your desired parameters +3. Call `build()` to get a `MessagesRequest` object +4. Call `fetchNext()` or `fetchPrevious()` to retrieve messages + +| Method | Description | +| --- | --- | +| `fetchNext()` | Returns messages after the specified parameters | +| `fetchPrevious()` | Returns messages before the specified parameters | + +Messages are paginated with a maximum of 100 per request. Calling `fetchPrevious()`/`fetchNext()` repeatedly on the same object gets subsequent pages. + +### MessagesRequestBuilder Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `uid` | `String?` | User ID to fetch conversation messages for | +| `guid` | `String?` | Group ID to fetch conversation messages for | +| `limit` | `int?` | Number of messages per request (max 100, default 30) | +| `messageId` | `int?` | Fetch messages before/after this message ID | +| `timestamp` | `DateTime?` | Fetch messages before/after this timestamp | +| `unread` | `bool?` | Fetch only unread messages | +| `hideMessagesFromBlockedUsers` | `bool?` | Exclude messages from blocked users (default `false`) | +| `searchKeyword` | `String?` | Search keyword to filter messages | +| `updatedAfter` | `DateTime?` | Fetch messages updated/received after this time | +| `updatesOnly` | `bool?` | Fetch only updated messages (use with `updatedAfter`) | +| `category` | `String?` | Single message category filter | +| `type` | `String?` | Single message type filter | +| `parentMessageId` | `int?` | Fetch messages in a specific thread | +| `hideReplies` | `bool?` | Exclude threaded messages (default `false`) | +| `hideDeleted` | `bool?` | Exclude deleted messages (default `false`) | +| `categories` | `List?` | Filter by multiple message categories | +| `types` | `List?` | Filter by multiple message types | +| `withTags` | `bool?` | Include tag information in response (default `false`) | +| `tags` | `List?` | Filter by specific tags | +| `hasMentions` | `bool?` | Fetch only messages with mentions (default `false`) | +| `hasLinks` | `bool?` | Fetch only messages with links (default `false`) | +| `hasAttachments` | `bool?` | Fetch only messages with attachments (default `false`) | +| `hasReactions` | `bool?` | Fetch only messages with reactions (default `false`) | +| `mentionedUids` | `List?` | Fetch messages mentioning specific users | +| `attachmentTypes` | `List?` | Filter by specific attachment types | +| `interactionGoalCompletedOnly` | `bool?` | Fetch only messages with completed interaction goals (default `false`) | +| `withParent` | `bool?` | Include parent message with replies (default `false`) | +| `hideQuotedMessages` | `bool?` | Exclude quoted messages (default `false`) | + +## Number of messages fetched + +*In other words, how do I set the number of messages fetched in a single iteration* + +To achieve this, you can use the `limit` property. This takes an integer value and informs the SDK to fetch the specified number of messages in one iteration. The maximum number of messages that can be fetched in one go is `100`. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..limit = 30).build(); +``` + + + + + +## Messages for a user conversation + +*In other words, how do I fetch messages between me and any user* + +Use the `uid` property to fetch messages between the logged-in user and a specific user. + + + +```dart +String UID = "cometchat-uid-1"; +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..limit = 50).build(); +``` + + + + + +## Messages for a group conversation + +*In other words, how do I fetch messages for any group conversation* + +Use the `guid` property to fetch messages from a group. The logged-in user must be a member of the group. + + + +```dart +String GUID = "cometchat-uid-1"; +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..guid = GUID + ..limit = 50).build(); +``` + + + + + + +If neither `uid` nor `guid` is set, all messages for the logged-in user across all conversations will be fetched. All parameters below can be combined with `uid` or `guid`. + + +## Messages before/after a message + +*In other words, how do I fetch messages before or after a particular message* + +Use the `messageId` property. This provides messages only after/before the message-id based on if `fetchNext()` or `fetchPrevious()` is called. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..messageId = 50 + ..limit = 50).build(); +``` + + + + + +This property can be used along with `uid` or `guid` to fetch messages after/before any specific message-id for a particular user/group conversation. + +## Messages before/after a given time + +*In other words, how do I fetch messages before or after a particular date or time* + +Use the `timestamp` property. This takes a `DateTime` value and provides messages only after/before the timestamp based on if `fetchNext()` or `fetchPrevious()` is called. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..timestamp = DateTime.now() + ..limit = 50).build(); +``` + + + + + +This property can be used along with `uid` or `guid` to fetch messages after/before any specific date or time for a particular user/group conversation. + +## Unread messages + +*In other words, how do I fetch unread messages* + +Use the `unread` property set to `true` to return just the unread messages. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..unread = true + ..limit = 50).build(); +``` + + + + + +Combine with `guid` or `uid` to fetch unread messages for a specific conversation. + +## Exclude messages from blocked users + +*In other words, how do I fetch messages excluding the messages from the users I have blocked* + +Use the `hideMessagesFromBlockedUsers` property. If set to `true`, messages from users blocked by the logged-in user will be excluded. Default is `false`. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..hideMessagesFromBlockedUsers = true + ..limit = 50).build(); +``` + + + + + +This also works in group conversations where both users are members. + +## Updated and received messages + +*In other words, how do I fetch messages that have been received or updated after a particular date or time* + +Use the `updatedAfter` property with a `DateTime` value to return all messages that have been updated and the ones that have been sent/received after the specified time. Updated messages include those marked as read/delivered, edited, or deleted. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..updatedAfter = DateTime.now() + ..limit = 50).build(); +``` + + + + + +Useful for syncing messages with a local database — fetch only what's changed since your last sync. + +## Updated messages only + +*In other words, how do I fetch messages that have been updated after a particular date or time* + +Use the `updatesOnly` property set to `true` together with `updatedAfter` to get just the updated messages and not the messages sent/received after the specified time. This property must be used with `updatedAfter`. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..updatedAfter = DateTime.now() + ..updatesOnly = true + ..limit = 50).build(); +``` + + + + + +## Messages for multiple categories + +*In other words, how do I fetch messages belonging to multiple categories* + +We recommend before trying this, you refer to the [Message structure and hierarchy guide](/sdk/flutter/message-structure-and-hierarchy) to get familiar with the various categories of messages. + +Use the `categories` property with a list of category names to filter by message category. + + + +```dart +String UID = "cometchat-uid-1"; +List categories = []; +categories.add("message"); +categories.add("custom"); + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..categories = categories + ..limit = 50).build(); +``` + + + + + +The above snippet will help you get only the messages belonging to the `message` and `custom` category. This can also be used to disable certain categories of messages like `call` and `action`. This along with `uid` and `guid` can help display only the messages you wish to display avoiding the other category of messages. + +## Messages for multiple types + +*In other words, how do I fetch messages belonging to multiple types* + +We recommend you refer to the [Message structure and hierarchy guide](/sdk/flutter/message-structure-and-hierarchy) to get familiar with the various types of messages before trying this out. + +Use the `types` property with a list of type names to filter by message type. + + + +```dart +String UID = "cometchat-uid-1"; +List types = []; +types.add("image"); +types.add("video"); + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..types = types + ..limit = 50).build(); +``` + + + + + +Using the above code snippet, you can fetch all the media messages. This along with `uid` or `guid` can be used to fetch media messages for any particular conversation. This can be useful in many other scenarios as well. + +## Messages for a specific thread + +*In other words, how do I fetch messages that are a part of a thread and not directly a user/group conversations* + +Use the `parentMessageId` property when you have implemented threaded conversations. This will return only messages belonging to the thread with the specified parent ID. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..parentMessageId = 103 + ..limit = 50).build(); +``` + + + + + +The above code snippet returns the messages that belong to the thread with parent id 103. + +## Hide threaded messages in user/group conversations + +*In other words, how do I exclude threaded messages from the normal user/group conversations* + +Use the `hideReplies` property set to `true` to exclude messages that belong to threads. Default is `false`. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..hideReplies = true + ..limit = 50).build(); +``` + + + + + +## Hide deleted messages in user/group conversations + +*In other words, how do I exclude deleted messages a user/group conversations* + +Use the `hideDeleted` property set to `true` to exclude deleted messages. Default is `false`. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..hideDeleted = true + ..limit = 50).build(); +``` + + + + + +## Hide quoted messages in user/group conversations + +*In other words, how do I exclude quoted messages from user/group conversations* + +Use the `hideQuotedMessages` property set to `true` to exclude quoted messages. Default is `false`. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..hideQuotedMessages = true + ..limit = 50).build(); +``` + + + + + +## Messages by tags + +*In other words, how do I fetch messages belonging to specific tags* + +Use the `tags` property with a list of tag names to fetch only messages with those tags. + + + +```dart +String UID = "cometchat-uid-1"; +List tags = []; +tags.add("tag1"); +tags.add("tag2"); + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..tags = tags + ..limit = 50).build(); +``` + + + + + +## Messages with tags + +*In other words, how do I fetch messages with the tags information* + +Use the `withTags` property set to `true` to include tag information in the response. Default is `false`. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..withTags = true + ..limit = 50).build(); +``` + + + + + +When `withTags` is set to `true`, each message's tags field will be populated. + +| Additional Field | Type | Description | +| --- | --- | --- | +| tags | `List` | Tags associated with the message | + +## Messages with links + +*In other words, as a logged-in user, how do I fetch messages that contain links?* + +Use the `hasLinks` property set to `true` to fetch only messages containing links. Default is `false`. + + + +This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..hasLinks = true + ..limit = 50).build(); +``` + + + + + +## Messages with attachments + +*In other words, as a logged-in user, how do I fetch messages that contain attachments?* + +Use the `hasAttachments` property set to `true` to fetch only messages with attachments (image, audio, video, or file). Default is `false`. + + + +This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..hasAttachments = true + ..limit = 50).build(); +``` + + + + + +## Messages with reactions + +*In other words, as a logged-in user, how do I fetch messages that contain reactions?* + +Use the `hasReactions` property set to `true` to fetch only messages with reactions. Default is `false`. + + + +This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..hasReactions = true + ..limit = 50).build(); +``` + + + + + +## Messages with mentions + +*In other words, as a logged-in user, how do I fetch messages that contain mentions?* + +Use the `hasMentions` property set to `true` to fetch only messages with mentions. Default is `false`. + + + +This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..hasMentions = true + ..limit = 50).build(); +``` + + + + + +## Messages with particular user mentions + +*In other words, as a logged-in user, how do I fetch messages that mention specific users?* + +Use the `mentionedUids` property with a list of UIDs to fetch messages that mention specific users. + + + +This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + + + + +```dart +String UID = "cometchat-uid-1"; +List mentionedUIDs = []; +mentionedUIDs.add("cometchat-uid-1"); +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..mentionedUids = mentionedUIDs + ..limit = 50).build(); +``` + + + + + +## Messages with specific attachment types + +*In other words, as a logged-in user, how do I fetch messages with specific types of attachments?* + +Use the `attachmentTypes` property with a list of attachment type values to fetch messages with specific attachment types. + + + +This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..attachmentTypes = ["image", "video"] + ..limit = 50).build(); +``` + + + + + +--- + +## Next Steps + + + + Handle incoming messages in real-time with listeners + + + Fetch and display conversation lists with filtering options + + + Understand message categories, types, and hierarchy + + + Work with message threads and replies + + diff --git a/sdk/flutter/v4/ai-agents.mdx b/sdk/flutter/v4/ai-agents.mdx new file mode 100644 index 000000000..173304cf6 --- /dev/null +++ b/sdk/flutter/v4/ai-agents.mdx @@ -0,0 +1,216 @@ +--- +title: "AI Agents" +sidebarTitle: "AI Agents" +description: "Learn how to integrate AI Agents in your Flutter app to enable intelligent, automated interactions that process user messages, trigger tools, and respond with contextually relevant information." +--- + + + +| Feature | Description | +| --- | --- | +| [AI Agents](#agent-run-lifecycle-and-message-flow) | Intelligent automated conversations with real-time streaming | +| [AI Moderation](/sdk/flutter/ai-moderation) | Automatic content moderation with `PENDING` → `APPROVED` / `DISAPPROVED` flow | +| [AI User Copilot](/fundamentals/ai-user-copilot/overview) | Smart Replies, Conversation Starter, Conversation Summary (Dashboard-enabled) | + +```dart +// Add AI Assistant listener for real-time events +CometChat.addAIAssistantListener("LISTENER_ID", AIAssistantListener( + onAIAssistantEventReceived: (AIAssistantBaseEvent event) { + debugPrint("AI Event: ${event.type}"); + }, +)); + +// Add Message listener for agentic messages +CometChat.addMessageListener("LISTENER_ID", MessageListener( + onAIAssistantMessageReceived: (AIAssistantMessage msg) { + debugPrint("AI Reply: ${msg.text}"); + }, + onAIToolResultReceived: (AIToolResultMessage result) { + debugPrint("Tool Result: $result"); + }, +)); + +// Remove listeners when done +CometChat.removeAIAssistantListener("LISTENER_ID"); +CometChat.removeMessageListener("LISTENER_ID"); +``` + +**Prerequisites:** `CometChat.init()` + `CometChat.login()` completed, AI features enabled in [Dashboard](https://app.cometchat.com) +**Event flow:** Run Start → Tool Call(s) → Text Message Stream → Run Finished + + +AI Agents enable intelligent, automated interactions within your application. They process user messages, trigger tools, and respond with contextually relevant information. For a broader introduction, see the [AI Agents section](/ai-agents). + + +Agents only respond to text messages. + + +## Agent Run Lifecycle and Message Flow + +When a user sends a text message to an Agent: +1. The platform starts a run and streams real-time events via `AIAssistantListener` +2. After the run completes, persisted Agentic Messages arrive via `MessageListener` + +### Real-time Events + +Events arrive via `onAIAssistantEventReceived` in this order: + +| Order | Event | Description | +|-------|-------|-------------| +| 1 | Run Start | A new run has begun | +| 2 | Tool Call Start | Agent decided to invoke a tool | +| 3 | Tool Call Arguments | Arguments being passed to the tool | +| 4 | Tool Call End | Tool execution completed | +| 5 | Tool Call Result | Tool's output is available | +| 6 | Text Message Start | Agent started composing a reply | +| 7 | Text Message Content | Streaming content chunks (multiple) | +| 8 | Text Message End | Agent reply is complete | +| 9 | Run Finished | Run finalized; persisted messages follow | + + +`Run Start` and `Run Finished` are always emitted. Tool Call events only appear when tools are invoked — there can be multiple tool call cycles in a single run. Text Message events are always emitted and carry the assistant's reply incrementally. + + +### Event Object Properties + +Every event is an [`AIAssistantBaseEvent`](/sdk/reference/messages#aiassistantbaseevent) with these common properties: + +| Getter | Return Type | Description | +|--------|-------------|-------------| +| `type` | `String` | Event type (e.g., `run_started`, `text_message_content`) | +| `conversationId` | `String` | The conversation this event belongs to | +| `messageId` | `String` | The message ID associated with the event | +| `parentMessageId` | `String` | Parent message ID (for threaded messages) | +| `runId` | `String` | The run ID for this agent execution | +| `threadId` | `String` | The thread ID for this agent execution | +| `timestamp` | `int` | Timestamp of the event | +| `data` | `Map` | Full event data payload | + +Some events carry additional data: + +| Event | Extra Property | Description | +|-------|---------------|-------------| +| Text Message Content | `delta` | The streaming text chunk for progressive rendering | +| Tool Call Arguments | `toolCallId`, `delta` | Tool call ID and argument chunk | +| Tool Call Result | `toolCallId`, `content`, `role` | Tool call ID, result content, and role | + + + + ```dart +import 'package:flutter/foundation.dart'; +import 'package:cometchat_sdk/cometchat_sdk.dart'; + +class AIAssistantEventHandler with AIAssistantListener { + final String _sdkStreamListenerId = "unique_listener_id"; + + /// Call this to start listening to AI Assistant events + void addListener() { + CometChat.addAIAssistantListener(_sdkStreamListenerId, this); + debugPrint("AIAssistantListener added successfully."); + } + + /// Call this to remove the AI Assistant listener + void removeListener() { + CometChat.removeAIAssistantListener(_sdkStreamListenerId); + debugPrint("AIAssistantListener removed successfully."); + } + + @override + void onAIAssistantEventReceived(AIAssistantBaseEvent aiAssistantBaseEvent) { + debugPrint( + "Received AI Event: ${aiAssistantBaseEvent.type} for Run ID: ${aiAssistantBaseEvent.id}", + ); + } +} + ``` + + + + +Always remove AI Assistant listeners when they're no longer needed (e.g., on widget dispose or page navigation). Failing to remove listeners can cause memory leaks and duplicate event handling. + + +#### Event descriptions +- Run Start: A new run has begun for the user's message. +- Tool Call Start: The agent decided to invoke a tool. +- Tool Call Arguments: Arguments being passed to the tool. +- Tool Call End: Tool execution completed. +- Tool Call Result: Tool's output is available. +- Text Message Start: The agent started composing a reply. +- Text Message Content: Streaming content chunks for progressive rendering. +- Text Message End: The agent reply is complete. +- Run Finished: The run is finalized; persisted messages will follow. + +### Agentic Messages + +After the run completes, these messages arrive via `MessageListener`: + +| Message Type | Description | +|--------------|-------------| +| `AIAssistantMessage` | The full assistant reply | +| `AIToolResultMessage` | The final output of a tool call | +| `AIToolArgumentMessage` | The arguments passed to a tool | + +Each message type extends `BaseMessage` and has a typed data accessor: + +| Message Type | Data Getter | Data Properties | +|--------------|-------------|-----------------| +| `AIAssistantMessage` | `getAssistantMessageData()` | `runId`, `threadId`, `text` | +| `AIToolResultMessage` | `getToolResultMessageData()` | `runId`, `threadId`, `text`, `toolCallId` | +| `AIToolArgumentMessage` | `getToolArgumentMessageData()` | `runId`, `threadId`, `toolCalls` | + +The `toolCalls` on `AIToolArgumentMessage` returns a list of `AIToolCall` objects, each with: + +| Property | Type | Description | +|----------|------|-------------| +| `id` | `String` | Unique tool call ID | +| `type` | `String` | Tool call type | +| `function` | `AIToolCallFunction` | Function object with `name` and `arguments` | +| `displayName` | `String` | Display name of the tool | +| `executionText` | `String` | Execution description text | + + + + ```dart + const listenerId = "unique_listener_id"; + + class Class_Name with MessageListener { + // Adding the MessageListener + // CometChat.addMessageListener(listenerId, this); + @override + void onAIAssistantMessageReceived(AIAssistantMessage aiAssistantMessage) { + debugPrint("AI Assistant Message Received: ${aiAssistantMessage.toString()}"); + } + + @override + void onAIToolResultReceived(AIToolResultMessage aiToolArgumentMessage) { + debugPrint("AI Assistant Message Received: ${aiToolArgumentMessage.toString()}"); + } + + @override + void onAIToolArgumentsReceived(AIToolArgumentMessage aiToolArgumentMessage) { + debugPrint("AI Assistant Message Received: ${aiToolArgumentMessage.toString()}"); + } + } + ``` + + + +--- + +## Next Steps + + + + Configure and deploy AI chatbots for automated conversations + + + Implement AI-powered content moderation for your chat + + + AI-powered features like smart replies and conversation summaries + + + Send text messages that trigger AI Agent responses + + diff --git a/sdk/flutter/v4/ai-chatbots-overview.mdx b/sdk/flutter/v4/ai-chatbots-overview.mdx new file mode 100644 index 000000000..f3037d7e9 --- /dev/null +++ b/sdk/flutter/v4/ai-chatbots-overview.mdx @@ -0,0 +1,65 @@ +--- +title: "Bots" +sidebarTitle: "AI Bots" +description: "Configure AI-powered chatbots to provide automated assistance and maintain conversational momentum in your Flutter app." +url: "/ai-chatbots/overview" +--- + + + +```dart +// AI Bots are configured via the CometChat Dashboard +// Navigate to: AI Agents > Custom Bots > AI Bots + +// Configuration options: +// 1. Set GPT Model (e.g., gpt-4, gpt-3.5-turbo) +// 2. Add your OpenAI API Key +// 3. Set Custom Instructions for bot behavior +// 4. Configure Temperature (0-1) for response creativity +// 5. Enable AI toggle + +// Once configured, bots respond automatically to user messages +// No additional SDK code required - bots work via CometChat backend +``` + +**Dashboard Path:** [CometChat Dashboard](https://app.cometchat.com) → Your App → AI Agents → Custom Bots → AI Bots + + + +AI Bots provide automated assistance to users seeking guidance or insights. Configure intelligent chatbots through the CometChat Dashboard to maintain conversational momentum in your Flutter application. + + + **Available via:** [Dashboard](https://app.cometchat.com) | [REST + API](https://api-explorer.cometchat.com) | UI Kits + + +--- + +## Next Steps + + + + Build custom AI agents that respond to user queries + + + Automatically moderate content using AI + + + Enhance user experience with AI-powered assistance + + + Learn how to send messages that bots can respond to + + diff --git a/sdk/flutter/v4/ai-moderation.mdx b/sdk/flutter/v4/ai-moderation.mdx new file mode 100644 index 000000000..4d8868826 --- /dev/null +++ b/sdk/flutter/v4/ai-moderation.mdx @@ -0,0 +1,317 @@ +--- +title: "AI Moderation" +sidebarTitle: "AI Moderation" +description: "Learn how to implement AI-powered content moderation in your Flutter app using CometChat SDK to automatically review messages for inappropriate content." +--- + + + +```dart +// Send message and check moderation status +CometChat.sendMessage(textMessage, onSuccess: (TextMessage message) { + if (message.moderationStatus?.value == ModerationStatusEnum.PENDING.value) { + // Message is under moderation review + } +}, onError: (e) {}); + +// Listen for moderation results +CometChat.addMessageListener("MODERATION_LISTENER", MessageListener( + onMessageModerated: (BaseMessage message) { + // Handle APPROVED or DISAPPROVED status + }, +)); + +// Remove listener when done +CometChat.removeMessageListener("MODERATION_LISTENER"); +``` + +**Moderation Status:** `PENDING` → `APPROVED` or `DISAPPROVED` +**Supported Types:** Text, Image, Video messages + + +## Overview + +AI Moderation in the CometChat SDK helps ensure that your chat application remains safe and compliant by automatically reviewing messages for inappropriate content. This feature leverages AI to moderate messages in real-time, reducing manual intervention and improving user experience. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) | [Dashboard](https://app.cometchat.com) + + + +For a broader understanding of moderation features, configuring rules, and managing flagged messages, see the [Moderation Overview](/moderation/overview). + + +## Prerequisites + +Before using AI Moderation, ensure the following: + +1. Moderation is enabled for your app in the [CometChat Dashboard](https://app.cometchat.com) +2. Moderation rules are configured under **Moderation > Rules** +3. You're using CometChat SDK version that supports moderation + +## How It Works + +```mermaid +sequenceDiagram + participant App + participant SDK + participant CometChat + participant Moderation + + App->>SDK: sendMessage() + SDK->>CometChat: Send Message + CometChat->>Moderation: Process Message + CometChat-->>SDK: Message (status: PENDING) + SDK-->>App: onSuccess (PENDING) + Moderation-->>CometChat: Moderation Result + CometChat-->>SDK: onMessageModerated + SDK-->>App: APPROVED or DISAPPROVED +``` + +| Step | Description | +|------|-------------| +| 1. Send Message | App sends a text, image, or video message | +| 2. Pending Status | Message is sent with `PENDING` moderation status | +| 3. AI Processing | Moderation service analyzes the content | +| 4. Result Event | `onMessageModerated` event fires with final status | + +## Supported Message Types + +Moderation is triggered **only** for the following message types: + +| Message Type | Moderated | Notes | +|--------------|-----------|-------| +| Text Messages | ✅ | Content analyzed for inappropriate text | +| Image Messages | ✅ | Images scanned for unsafe content | +| Video Messages | ✅ | Videos analyzed for prohibited content | + +Moderation applies to [`TextMessage`](/sdk/reference/messages#textmessage) and [`MediaMessage`](/sdk/reference/messages#mediamessage) types. +| Custom Messages | ❌ | Not subject to AI moderation | +| Action Messages | ❌ | Not subject to AI moderation | + +## Moderation Status + +The `moderationStatus` property returns one of the following enum values: + +| Status | Enum Value | Description | +|--------|------------|-------------| +| Pending | `ModerationStatusEnum.PENDING` | Message is being processed by moderation | +| Approved | `ModerationStatusEnum.APPROVED` | Message passed moderation and is visible | +| Disapproved | `ModerationStatusEnum.DISAPPROVED` | Message violated rules and was blocked | + +## Implementation + +### Step 1: Send a Message and Check Initial Status + +When you send a text, image, or video message, check the initial moderation status: + + + + ```dart + TextMessage textMessage = TextMessage( + text: "Hello, how are you?", + receiverUid: receiverUID, + receiverType: ReceiverTypeConstants.user, + ); + + CometChat.sendMessage( + textMessage, + onSuccess: (TextMessage message) { + // Check moderation status + if (message.moderationStatus?.value == ModerationStatusEnum.PENDING.value) { + print("Message is under moderation review"); + // Show pending indicator in UI + } + }, + onError: (CometChatException e) { + print("Message sending failed: ${e.message}"); + }, + ); + ``` + + + + +**On Success** — A `TextMessage` object containing all details of the sent message, including the initial moderation status: + + + +**TextMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `501` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#send-moderated-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#send-moderated-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `text` | string | The text content of the message | `"Hello, how are you?"` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `moderationStatus` | string | Moderation status of the message | `"pending"` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_BLOCKED_BY_EXTENSION"` | +| `message` | string | Human-readable error message | `"Message blocked by moderation."` | +| `details` | string | Additional technical details | `"The message was flagged and blocked by the AI moderation extension."` | + + + +### Step 2: Listen for Moderation Results + +Implement the `MessageListener` to receive moderation results in real-time: + + + + ```dart + class ModerationListener with MessageListener { + + @override + void onMessageModerated(BaseMessage message) { + if (message is TextMessage) { + switch (message.moderationStatus?.value) { + case ModerationStatusEnum.APPROVED: + print("Message ${message.id} approved"); + // Update UI to show message normally + break; + + case ModerationStatusEnum.DISAPPROVED: + print("Message ${message.id} blocked"); + // Handle blocked message (hide or show warning) + handleDisapprovedMessage(message); + break; + } + } else if (message is MediaMessage) { + switch (message.moderationStatus?.value) { + case ModerationStatusEnum.APPROVED: + print("Media message ${message.id} approved"); + break; + + case ModerationStatusEnum.DISAPPROVED: + print("Media message ${message.id} blocked"); + handleDisapprovedMessage(message); + break; + } + } + } + } + + // Register the listener + CometChat.addMessageListener("MODERATION_LISTENER", ModerationListener()); + + // Don't forget to remove the listener when done + // CometChat.removeMessageListener("MODERATION_LISTENER"); + ``` + + + +### Step 3: Handle Disapproved Messages + +When a message is disapproved, handle it appropriately in your UI: + + + + ```dart + void handleDisapprovedMessage(BaseMessage message) { + int messageId = message.id; + + // Option 1: Hide the message completely + hideMessageFromUI(messageId); + + // Option 2: Show a placeholder message + showBlockedPlaceholder(messageId, "This message was blocked by moderation"); + + // Option 3: Notify the sender (if it's their message) + if (message.sender?.uid == currentUserUID) { + showNotification("Your message was blocked due to policy violation"); + } + } + ``` + + + +## Next Steps + +After implementing AI Moderation, explore these related features: + + + + Build intelligent AI-powered agents for automated conversations + + + Allow users to manually report inappropriate messages + + + Create automated chatbot experiences for your users + + + Handle incoming messages and moderation events + + diff --git a/sdk/flutter/v4/ai-user-copilot-overview.mdx b/sdk/flutter/v4/ai-user-copilot-overview.mdx new file mode 100644 index 000000000..12601d050 --- /dev/null +++ b/sdk/flutter/v4/ai-user-copilot-overview.mdx @@ -0,0 +1,6 @@ +--- +title: "AI" +sidebarTitle: "AI User Copilot" +description: "Enhance your Flutter app with AI-powered features like Smart Replies, Conversation Starters, and Conversation Summaries using CometChat." +url: "/fundamentals/ai-user-copilot/overview" +--- diff --git a/sdk/flutter/v4/authentication-overview.mdx b/sdk/flutter/v4/authentication-overview.mdx new file mode 100644 index 000000000..07ff5d486 --- /dev/null +++ b/sdk/flutter/v4/authentication-overview.mdx @@ -0,0 +1,348 @@ +--- +title: "Authentication" +sidebarTitle: "Authentication" +description: "Create users, log in with Auth Key or Auth Token, check login status, and log out using the CometChat Flutter SDK." +--- + +{/* TL;DR for Agents and Quick Reference */} + + +```dart +// Check existing session +User? user = await CometChat.getLoggedInUser(); + +// Login with Auth Key (development only) +CometChat.login("cometchat-uid-1", "AUTH_KEY", + onSuccess: (User user) { debugPrint("Logged in: $user"); }, + onError: (CometChatException e) { debugPrint("Error: ${e.message}"); } +); + +// Login with Auth Token (production) +CometChat.loginWithAuthToken("AUTH_TOKEN", + onSuccess: (User user) { debugPrint("Logged in: $user"); }, + onError: (CometChatException e) { debugPrint("Error: ${e.message}"); } +); + +// Logout +CometChat.logout( + onSuccess: (String msg) { debugPrint("Logged out"); }, + onError: (CometChatException e) { debugPrint("Error: ${e.message}"); } +); +``` + +**Create users via:** [Dashboard](https://app.cometchat.com) (testing) | [REST API](https://api-explorer.cometchat.com/reference/creates-user) (production) +**Test UIDs:** `cometchat-uid-1` through `cometchat-uid-5` + + +After [initializing](/sdk/flutter/setup) the SDK, the next step is to authenticate your user. CometChat provides two login methods — Auth Key for quick development, and Auth Token for production — both accessed through the `login()` method. + +### How It Works + +```mermaid +sequenceDiagram + participant User + participant YourApp as Your App + participant YourServer as Your Server + participant CometChat as CometChat + + User->>YourApp: Signs up / Logs in + YourApp->>YourServer: Authenticate user + YourServer->>CometChat: Create user (REST API, first time only) + CometChat-->>YourServer: User created + YourServer->>CometChat: Create Auth Token (REST API) + CometChat-->>YourServer: Auth Token + YourServer-->>YourApp: Return Auth Token + YourApp->>CometChat: CometChat.loginWithAuthToken(authToken) + CometChat-->>YourApp: User object (logged in) +``` + +## Before You Log In + +### Create a User + +A user must exist in CometChat before they can log in. + +- **During development:** Create users from the [CometChat Dashboard](https://app.cometchat.com). Five test users are already available with UIDs `cometchat-uid-1` through `cometchat-uid-5`. +- **In production:** Call the [Create User REST API](https://api-explorer.cometchat.com/reference/creates-user) when a user signs up in your app. + + + +We have setup 5 users for testing having UIDs: `cometchat-uid-1`, `cometchat-uid-2`, `cometchat-uid-3`, `cometchat-uid-4` and `cometchat-uid-5`. + + + +### Check for an Existing Session + +The SDK persists the logged-in user's session locally. Before calling `login()`, always check whether a session already exists — this avoids unnecessary login calls and keeps your app responsive. + +```dart +User? user = await CometChat.getLoggedInUser(); +if (user != null) { + // User is already logged in — proceed to your app +} +``` + +If `getLoggedInUser()` returns `null`, no active session exists and you need to call `login()`. + +| Method | Returns | Description | +| ------ | ------- | ----------- | +| `CometChat.getLoggedInUser()` | `Future` | Returns the currently logged-in user, or `null` if no session exists | + + +## Login with Auth Key + +This straightforward authentication method is ideal for proof-of-concept (POC) development or during the early stages of application development. For production environments, however, we strongly recommend using an [Auth Token](#login-with-auth-token) instead of an Auth Key to ensure enhanced security. + + +Auth Keys are meant for development and testing only. For production, use [Auth Token login](#login-with-auth-token) instead. Never ship Auth Keys in client-side code. + + + + +```dart +String UID = "user_id"; // Replace with the UID of the user to login +String authKey = "AUTH_KEY"; // Replace with your App Auth Key + +final user = await CometChat.getLoggedInUser(); +if (user == null) { +await CometChat.login(UID, authKey, + onSuccess: (User user) { + debugPrint("Login Successful : $user" ); + }, onError: (CometChatException e) { + debugPrint("Login failed with exception: ${e.message}"); + }); +} +``` + + + + + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `UID` | `String` | The UID of the user to log in | +| `authKey` | `String` | Your CometChat Auth Key | + +On success, the `onSuccess` callback receives a [`User`](/sdk/reference/entities#user) object containing the logged-in user's details. + + +**On Success** — A `User` object representing the logged-in user: + + + +**User Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +**CometChatException:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified UID does not exist."` | +| `details` | string | Additional technical details | `"Please verify the UID and try again."` | + + + +## Login with Auth Token + +Auth Token login keeps your Auth Key off the client entirely. Your server generates a token via the REST API and passes it to the client. + +1. [Create the user](https://api-explorer.cometchat.com/reference/creates-user) via the REST API when they sign up (first time only). +2. [Generate an Auth Token](https://api-explorer.cometchat.com/reference/create-authtoken) on your server and return it to the client. +3. Pass the token to `loginWithAuthToken()`. + + + +```dart +String authToken = "AUTH_TOKEN"; +var user = await CometChat.getLoggedInUser(onSuccess: (_){}, onError: (_){}); +if (user == null) { + if(authToken!=null){ + await CometChat.loginWithAuthToken(authToken, + onSuccess: (User loggedInUser){ + debugPrint("Login Successful : $user" ); + }, onError: ( CometChatException e){ + debugPrint("Login failed with exception: ${e.message}"); + }); + } +} +``` + + + + + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `authToken` | `String` | Auth Token generated on your server for the user | + +On success, the `onSuccess` callback receives a [`User`](/sdk/reference/entities#user) object containing the logged-in user's details. + + +**On Success** — A `User` object representing the logged-in user: + + + +**User Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +**CometChatException:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified UID does not exist."` | +| `details` | string | Additional technical details | `"Please verify the UID and try again."` | + + + +## Logout + +Call `logout()` when your user logs out of your app. This clears the local session. + + + +```dart +CometChat.logout( onSuccess: ( successMessage) { + debugPrint("Logout successful with message $successMessage"); + }, onError: (CometChatException e ){ + debugPrint("Logout failed with exception: ${e.message}"); + } + ); +``` + + + + + + +**On Success** — A `String` message confirming the logout: + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"Logout successful"` | + + + + + +**CometChatException:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_NOT_LOGGED_IN"` | +| `message` | string | Human-readable error message | `"No active user session."` | +| `details` | string | Additional technical details | `"Please log in before attempting to log out."` | + + + +--- + +## Login Listener + +You can listen for login and logout events in real time using `LoginListener`. This is useful for updating UI state or triggering side effects when the auth state changes. + +| Callback | Description | +| --- | --- | +| `loginSuccess(User user)` | User logged in successfully. Provides the `User` object. | +| `loginFailure(CometChatException e)` | Login failed. Provides a `CometChatException`. | +| `logoutSuccess()` | User logged out successfully. | +| `logoutFailure(CometChatException e)` | Logout failed. Provides a `CometChatException`. | + +### Add a Listener + + + +```dart +class Class_Name with LoginListener { + + // CometChat.addLoginListener("UNIQUE_LISTENER_ID", this); // call this in initState + + @override + void loginSuccess(User user) { + debugPrint("LoginListener loginSuccess: $user"); + } + + @override + void loginFailure(CometChatException e) { + debugPrint("LoginListener loginFailure: ${e.message}"); + } + + @override + void logoutSuccess() { + debugPrint("LoginListener logoutSuccess"); + } + + @override + void logoutFailure(CometChatException e) { + debugPrint("LoginListener logoutFailure: ${e.message}"); + } +} +``` + + + +### Remove a Listener + +```dart +CometChat.removeLoginListener("UNIQUE_LISTENER_ID"); +``` + + +Always remove login listeners when they're no longer needed (e.g., on widget disposal or when navigating away). Failing to remove listeners can cause memory leaks and duplicate event handling. + + +--- + +## Next Steps + + + + Send your first text, media, or custom message + + + Create, update, and manage users in your app + + + Monitor the SDK connection state in real time + + + Complete reference for all SDK event listeners + + diff --git a/sdk/flutter/best-practices.mdx b/sdk/flutter/v4/best-practices.mdx similarity index 100% rename from sdk/flutter/best-practices.mdx rename to sdk/flutter/v4/best-practices.mdx diff --git a/sdk/flutter/v4/block-users.mdx b/sdk/flutter/v4/block-users.mdx new file mode 100644 index 000000000..6370279cd --- /dev/null +++ b/sdk/flutter/v4/block-users.mdx @@ -0,0 +1,307 @@ +--- +title: "Block Users" +sidebarTitle: "Block Users" +description: "Learn how to block and unblock users in your Flutter app using the CometChat SDK to manage user interactions and privacy." +--- + + + +```dart +// Block users +List uids = ["UID1", "UID2"]; +CometChat.blockUser(uids, onSuccess: (Map map) { + debugPrint("Blocked: $map"); +}, onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); +}); + +// Unblock users +CometChat.unblockUser(uids, onSuccess: (Map map) { + debugPrint("Unblocked: $map"); +}, onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); +}); + +// Get blocked users list +BlockedUsersRequest request = (BlockedUsersRequestBuilder()..limit = 30).build(); +request.fetchNext(onSuccess: (List users) { }, onError: (e) { }); +``` + +**Directions:** `directionBlockedByMe` | `directionHasBlockedMe` | `directionBoth` (default) + + +Block users to prevent them from sending messages to the logged-in user. This feature helps users manage their privacy and control who can communicate with them. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Block Users + +*In other words, as a logged-in user, how do I block a user from sending me messages?* + +You can block users using the `blockUser()` method. Once any user is blocked, all the communication to and from the respective user will be completely blocked. You can block multiple users in a single operation. The `blockUser()` method takes a `List` as a parameter which holds the list of `UIDs` to be blocked. + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `uids` | `List?` | List of UIDs of users to block | +| `onSuccess` | `Function(Map map)?` | Callback triggered on successful block operation | +| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | + + + +```dart +List uids = []; +uids.add("UID1"); +uids.add("UID2"); +uids.add("UID3"); +CometChat.blockUser(uids, onSuccess: (Map map) { +debugPrint("Blocked User Successfully $map "); +}, onError: (CometChatException e) { +debugPrint("Blocked User Unsuccessful ${e.message} "); +}); +``` + + + + + + +**On Success** — A `Map` containing UIDs as keys and `"success"` or `"fail"` as values indicating the result of each block operation: + + + +**Map Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `UID1` | string | Block result for UID1 | `"success"` | +| `UID2` | string | Block result for UID2 | `"success"` | +| `UID3` | string | Block result for UID3 | `"success"` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified UID does not exist."` | +| `details` | string | Additional technical details | `"Please verify the UID and try again."` | + + + +In the `onSuccess()` callback, you receive a Map which contains `UIDs` as the keys and `success` or `fail` as the value based on if the block operation for the `UID` was successful or not. + +## Unblock Users + +*In other words, as a logged-in user, how do I unblock a user I previously blocked?* + +You can unblock the already blocked users using the `unblockUser()` method. You can unblock multiple users in a single operation. The `unblockUser()` method takes a `List` as a parameter which holds the list of `UIDs` to be unblocked. + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `uids` | `List?` | List of UIDs of users to unblock | +| `onSuccess` | `Function(Map map)?` | Callback triggered on successful unblock operation | +| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | + + + +```dart +List uids = []; +uids.add("UID1"); +uids.add("UID2"); +uids.add("UID3"); + +CometChat.unblockUser(uids, onSuccess: (Map map) { + debugPrint("Blocked User Successfully $map "); + }, onError: (CometChatException e) { + debugPrint("Blocked User Unsuccessful ${e.message} "); + }); +``` + + + + + + +**On Success** — A `Map` containing UIDs as keys and `"success"` or `"fail"` as values indicating the result of each unblock operation: + + + +**Map Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `UID1` | string | Unblock result for UID1 | `"success"` | +| `UID2` | string | Unblock result for UID2 | `"success"` | +| `UID3` | string | Unblock result for UID3 | `"success"` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified UID does not exist."` | +| `details` | string | Additional technical details | `"Please verify the UID and try again."` | + + + +In the `onSuccess()` callback, you receive a Map which contains `UIDs` as the keys and `success` or `fail` as the value based on if the unblock operation for the `UID` was successful or not. + +## Get list of blocked users + +*In other words, as a logged-in user, how do I get a list of all users I've blocked?* + +In order to fetch the list of blocked users, you can use the `BlockedUsersRequest` class. To use this class i.e to create an object of the `BlockedUsersRequest class`, you need to use the `BlockedUsersRequestBuilder` class. The `BlockedUsersRequestBuilder` class allows you to set the parameters based on which the blocked users are to be fetched. + +The `BlockedUsersRequestBuilder` class allows you to set the below parameters: + +### BlockedUsersRequestBuilder + +| Parameter | Type | Description | +|-----------|------|-------------| +| `limit` | `int?` | Number of blocked users to fetch per request. Max: 100, Default: 30 | +| `searchKeyword` | `String?` | Keyword to filter blocked users by name | +| `direction` | `String?` | Direction of block — `directionBlockedByMe`, `directionHasBlockedMe`, or `directionBoth` (default) | +| `setPage` | `int?` | Fetch blocked users from a particular page | + +### Set Limit + +This method sets the limit i.e. the number of blocked users that should be fetched in a single iteration. + + + +```dart +BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() + ..limit = 50 + ).build(); +``` + + + + + +### Set Search Keyword + +This method allows you to set the search string based on which the blocked users are to be fetched. + + + +```dart +BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() + ..limit = 50 + ..searchKeyword = "abc" + ).build(); +``` + + + + + +### Set Direction + +* `CometChatBlockedUsersDirection.`*directionBlockedByMe* - This will ensure that the list of blocked users only contains the users blocked by the logged in user. +* `CometChatBlockedUsersDirection.`*directionHasBlockedMe* - This will ensure that the list of blocked users only contains the users that have blocked the logged in user. +* `CometChatBlockedUsersDirection.`*directionBoth* - This will make sure the list of users includes both the above cases. This is the default value for the direction variable if it is not set. + + + +```dart +BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() + ..limit = 50 + ..direction = CometChatBlockedUsersDirection.directionBlockedByMe + ).build(); +``` + + + + + +Finally, once all the parameters are set in the builder class, you need to call the `build()` method to get the object of the `BlockedUsersRequest` class. + +Once you have the object of the `BlockedUsersRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `User` objects containing n number of blocked users where N is the limit set in the builder class. + + + +```dart +BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() + ..limit = 30 + ).build(); + +blockedUsersRequest.fetchNext(onSuccess: (List userList){ + debugPrint("Custom Message Sent Successfully : $userList "); + }, onError: (CometChatException e){ + debugPrint("Blocked User Request failed with exception: ${e.message}"); +}); +``` + + + + + + +**On Success** — A `List` containing the blocked users: + + + +**User Object (per item in list):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-3"` | +| `name` | string | Display name of the user | `"Kevin Hart"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `true` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified UID does not exist."` | +| `details` | string | Additional technical details | `"Please verify the UID and try again."` | + + + +Relevant fields to access on returned [`User`](/sdk/reference/entities#user) objects: + +| Field | Type | Description | +|-------|------|-------------| +| `blockedByMe` | `bool` | Whether the logged-in user has blocked this user | +| `hasBlockedMe` | `bool` | Whether this user has blocked the logged-in user | + +--- + +## Next Steps + + + + Fetch and filter users from your CometChat app + + + Create, update, and delete users programmatically + + + Track online/offline status of users in real-time + + + Complete guide to user features in CometChat + + diff --git a/sdk/flutter/calling-overview.mdx b/sdk/flutter/v4/calling-overview.mdx similarity index 100% rename from sdk/flutter/calling-overview.mdx rename to sdk/flutter/v4/calling-overview.mdx diff --git a/sdk/flutter/v5/changelog.mdx b/sdk/flutter/v4/changelog.mdx similarity index 53% rename from sdk/flutter/v5/changelog.mdx rename to sdk/flutter/v4/changelog.mdx index eb92a1622..d482ab3bb 100644 --- a/sdk/flutter/v5/changelog.mdx +++ b/sdk/flutter/v4/changelog.mdx @@ -1,4 +1,6 @@ --- title: "Changelog" +sidebarTitle: "Changelog" url: "https://github.com/cometchat/chat-sdk-flutter/releases" +description: "Navigate to Changelog documentation." --- \ No newline at end of file diff --git a/sdk/flutter/v5/connection-behaviour.mdx b/sdk/flutter/v4/connection-behaviour.mdx similarity index 60% rename from sdk/flutter/v5/connection-behaviour.mdx rename to sdk/flutter/v4/connection-behaviour.mdx index ba5b683a9..fb2e027ee 100644 --- a/sdk/flutter/v5/connection-behaviour.mdx +++ b/sdk/flutter/v4/connection-behaviour.mdx @@ -1,8 +1,36 @@ --- title: "Connection Behaviour" +sidebarTitle: "Connection Behaviour" +description: "Understand how CometChat SDK manages WebSocket connections in auto and manual modes, including background behavior and reconnection handling." --- + +```dart +// Auto Mode (default) - SDK manages connection automatically +AppSettings appSettings = (AppSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.allUsers + ..region = "REGION" + ..autoEstablishSocketConnection = true // Default behavior +).build(); + +// Manual Mode - You control the connection +AppSettings appSettings = (AppSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.allUsers + ..region = "REGION" + ..autoEstablishSocketConnection = false // Manual control +).build(); + +// Manual mode methods +CometChat.connect(onSuccess: (msg) {}, onError: (e) {}); // Establish connection +CometChat.disconnect(onSuccess: (msg) {}, onError: (e) {}); // Break connection +CometChat.ping(onSuccess: () {}, onError: (e) {}); // Keep alive in background +``` + +**Connection Modes:** +- **Auto Mode:** SDK manages WebSocket automatically (foreground=connected, background=disconnected) +- **Manual Mode:** You control connect/disconnect; call `ping()` every 30s to keep background connection alive + ## Default SDK behaviour on login @@ -18,6 +46,8 @@ When the app is reopened, and the init() method is called, the web-socket connec This is the default behaviour of the CometChat SDKs. However, if you wish to take control of the web-socket connection i.e if you wish to connect and disconnect to the web-socket server manually, you can refer to the Managing Web-socket Connection section. +You'd want manual control when you need to conserve resources by connecting only when the user is actively chatting, or when you need precise control over when real-time events start flowing. + ## Auto Mode CometChat SDK default connection behaviour is auto mode. Auto mode, the SDK automatically establishes and maintains the WebSocket connection. You do not need to explicitly call any methods to do this. @@ -90,6 +120,25 @@ CometChat.init(appId, appSettings, onSuccess: (String successMessage) { + +**On Success** — A `String` message confirming SDK initialization: + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"Initialization completed successfully"` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"SDK initialization failed."` | +| `details` | string | Additional technical details | `"Please verify your App ID and region, then try again."` | + + + You can manage the connection to the web-socket server using the `connect()` and `disconnect()` methods provided by the SDK. ## Connect to the web-socket server @@ -113,6 +162,25 @@ CometChat.connect( + +**On Success** — A `String` message confirming the WebSocket connection was established: + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"Web socket connection successful"` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_NOT_LOGGED_IN"` | +| `message` | string | Human-readable error message | `"No active user session."` | +| `details` | string | Additional technical details | `"Please log in before establishing a WebSocket connection."` | + + + ## Disconnect from the web-socket server You can use the `disconnect()` method provided by the `CometChat` class to break the established connection. Once the connection is broken, you will stop receiving all the real-time events for the logged in user. @@ -134,6 +202,25 @@ CometChat.disconnect( + +**On Success** — A `String` message confirming the WebSocket connection was disconnected: + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"Web socket disconnection successful"` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_NOT_LOGGED_IN"` | +| `message` | string | Human-readable error message | `"No active user session."` | +| `details` | string | Additional technical details | `"Please log in before disconnecting the WebSocket connection."` | + + + ## Maintain long-standing background connection @@ -161,6 +248,44 @@ CometChat.ping( + +**On Success** — A confirmation that the ping was sent successfully (no return value): + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation | `"ping successful"` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_NOT_LOGGED_IN"` | +| `message` | string | Human-readable error message | `"No active user session."` | +| `details` | string | Additional technical details | `"Please log in before sending a ping to the server."` | + + + ## Reconnection If manual mode is enabled and the app is in the foreground, the SDK will automatically reconnect the WebSocket if the internet connection is lost. However, if the app is in the background and the WebSocket is disconnected or you called `CometChat.disconnect()`, then you will need to call the `CometChat.connect()` method to create a new WebSocket connection. + +--- + +## Next Steps + + + + Monitor real-time WebSocket connection status with listeners + + + Configure CometChat SDK initialization and settings + + + Learn about all available real-time event listeners + + + Implement user login and logout functionality + + diff --git a/sdk/flutter/v4/connection-status.mdx b/sdk/flutter/v4/connection-status.mdx new file mode 100644 index 000000000..890d70074 --- /dev/null +++ b/sdk/flutter/v4/connection-status.mdx @@ -0,0 +1,161 @@ +--- +title: "Connection Status" +sidebarTitle: "Connection Status" +description: "Monitor real-time WebSocket connection status with CometChat SDK using ConnectionListener callbacks and getConnectionStatus method." +--- + + + +```dart +// Add connection listener +CometChat.addConnectionListener("connection_listener", ConnectionListenerImpl()); + +// Connection listener implementation +class ConnectionListenerImpl with ConnectionListener { + @override + void onConnected() => debugPrint("Connected"); + + @override + void onConnecting() => debugPrint("Connecting..."); + + @override + void onDisconnected() => debugPrint("Disconnected"); + + @override + void onFeatureThrottled() => debugPrint("Feature throttled"); + + @override + void onConnectionError(CometChatException error) => debugPrint("Error: ${error.message}"); +} + +// Check current connection status +String status = CometChat.getConnectionStatus(); +// Returns: CometChatWSState.connected, connecting, disconnected, or featureThrottled + +// Remove listener when done +CometChat.removeConnectionListener("connection_listener"); +``` + + +The CometChat SDK maintains a WebSocket connection to CometChat servers for real-time events. You can check the current connection state and listen for changes — useful for showing connectivity indicators in your UI or queuing operations while offline. + +When the connection drops, the SDK automatically attempts to reconnect, cycling through `disconnected` → `connecting` → `connected`. + +## Connection States + +| Value | Callback | Description | +|-------|----------|-------------| +| `CometChatWSState.connected` | `onConnected()` | SDK has an active connection to CometChat servers | +| `CometChatWSState.connecting` | `onConnecting()` | SDK is attempting to establish or re-establish a connection | +| `CometChatWSState.disconnected` | `onDisconnected()` | SDK is disconnected due to network issues or other errors | +| `CometChatWSState.featureThrottled` | `onFeatureThrottled()` | A feature has been throttled to prevent performance loss | +| — | `onConnectionError(CometChatException)` | An error occurred while maintaining the connection | + +## Get Current Status + +Use `getConnectionStatus()` to check the current connection state at any time: + + + +```dart +String connectionStatus = CometChat.getConnectionStatus(); +``` + + + +The method returns one of the following values: + +1. `CometChatWSState.connected` (connected) +2. `CometChatWSState.connecting` (connecting) +3. `CometChatWSState.disconnected` (disconnected) +4. `CometChatWSState.featureThrottled` (featureThrottled) + +## Listen for Connection Changes + +Register a `ConnectionListener` to receive real-time connection state updates. We recommend adding this on app startup after `CometChat.init()` completes. + +### ConnectionListener Events + +| Event | Parameter | Description | +|-------|-----------|-------------| +| `onConnected()` | — | Triggered when the SDK successfully establishes a connection to the WebSocket server | +| `onConnecting()` | — | Triggered when the SDK is attempting to establish a connection to the WebSocket server | +| `onDisconnected()` | — | Triggered when the SDK gets disconnected due to network fluctuations or other issues | +| `onFeatureThrottled()` | — | Triggered when CometChat automatically toggles off certain features to prevent performance loss | +| `onConnectionError(CometChatException error)` | `CometChatException` | Triggered when an error occurs while maintaining the WebSocket connection | + + + + +```dart +class Class_Name with ConnectionListener { +//1. Register Connection listener +//CometChat.addConnctionListener("listenerId", this); + +//2. Ovveride the ConnectionListener methods +@override +void onConnected() { + // TODO: implement onConnected +} + +@override +void onConnecting() { + // TODO: implement onConnecting +} + +@override +void onDisconnected() { + // TODO: implement onDisconnected +} + +@override +void onFeatureThrottled() { + // TODO: implement onFeatureThrottled +} + +@override +void onConnectionError(CometChatException error) { + // TODO: implement onFeatureThrottled +} + +} +``` + + + + + + +Always remove connection listeners when they're no longer needed (e.g., on widget dispose or navigation). Failing to remove listeners can cause memory leaks and duplicate event handling. + + +### Remove Connection Listener + +```dart +CometChat.removeConnectionListener("listenerId"); +``` + + + +Know more about CometChat SDK connection behaviour [click here](/sdk/flutter/connection-behaviour) + + + +--- + +## Next Steps + + + + Understand how CometChat SDK manages WebSocket connections + + + Monitor user login and logout events in real-time + + + Complete reference for all SDK listeners + + + Install and initialize the CometChat SDK + + diff --git a/sdk/flutter/v4/create-group.mdx b/sdk/flutter/v4/create-group.mdx new file mode 100644 index 000000000..0c6157da2 --- /dev/null +++ b/sdk/flutter/v4/create-group.mdx @@ -0,0 +1,249 @@ +--- +title: "Create A Group" +sidebarTitle: "Create Group" +description: "Create public, private, or password-protected groups and optionally add members during creation using the CometChat Flutter SDK." +--- + + + +```dart +// Create a group +Group group = Group(guid: "GUID", name: "Group Name", type: CometChatGroupType.public); +await CometChat.createGroup( + group: group, + onSuccess: (Group group) => debugPrint("Created: ${group.name}"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Create group with members +CometChat.createGroupWithMembers( + group: group, + groupMembers: [GroupMember(uid: "UID", scope: GroupMemberScope.participant)], + bannedUserIds: [], + onSuccess: (Group group) => debugPrint("Created with members: ${group.name}"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); +``` + +**Group types:** `CometChatGroupType.public` | `CometChatGroupType.password` | `CometChatGroupType.private` +**Member scopes:** `GroupMemberScope.admin` | `GroupMemberScope.moderator` | `GroupMemberScope.participant` + + +Create groups for multi-user conversations. You can create a group on its own with `createGroup()`, or create one and add members in a single call with `createGroupWithMembers()`. See the [Group Class](#group-class) reference at the bottom for all available fields. + +## Create a Group + +*In other words, as a logged-in user, how do I create a public, private or password-protected group?* + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +Use `createGroup()` to create a new group. Pass a `Group` object with the group details. + +| Group Type | Constant | Description | +| --- | --- | --- | +| Public | `CometChatGroupType.public` | Any user can join | +| Password | `CometChatGroupType.password` | Users must provide the correct password | +| Private | `CometChatGroupType.private` | Users must be added by an admin/moderator | + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `group` | `Group` | An instance of the `Group` class containing group details (guid, name, type, password) | +| `onSuccess` | `Function(Group group)?` | Callback triggered on successful group creation | +| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | + + + +```dart +String GUID = "GUID"; + String groupName = "Hello Group!"; + String groupType = CometChatGroupType.public; + String password = ""; + + Group _group = Group(guid: GUID, name: groupName, type: groupType); + + + await CometChat.createGroup(group: _group, onSuccess: (Group group ){ + debugPrint("Group Created Successfully : $group "); + }, onError:(CometChatException e) { + debugPrint("Group Creation failed with exception: ${e.message}"); + } ); +``` + + + + + + +**On Success** — A `Group` object containing all details of the newly created group: + + + +**Group Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | +| `name` | string | Display name of the group | `"Hello Group!"` | +| `icon` | string | URL of the group icon | `null` | +| `description` | string | Description of the group | `null` | +| `membersCount` | number | Number of members in the group | `1` | +| `metadata` | object | Custom metadata attached to the group | `{}` | +| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | +| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | +| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | +| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | +| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | +| `tags` | array | List of tags associated with the group | `[]` | +| `type` | string | Type of the group (public, private, password) | `"public"` | +| `scope` | string | Scope of the logged-in user in the group | `"admin"` | +| `password` | string | Password for password-protected groups | `null` | +| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified group does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | + + + +On success, returns a [`Group`](/sdk/reference/entities#group) object with the created group's details. + + + +GUID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. + + + +## Add Members While Creating a Group + +Use `createGroupWithMembers()` to create a group and add members in one operation. + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `group` | `Group` | The `Group` object with group details (guid, name, type, password) | +| `groupMembers` | `List` | List of `GroupMember` objects to add during creation | +| `bannedUserIds` | `List` | List of UIDs to ban upon creation (defaults to empty) | +| `onSuccess` | `Function(Group group)?` | Callback triggered on successful creation | +| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | + +Create a `GroupMember` with: `GroupMember(uid: "UID", scope: GroupMemberScope.participant)` + + + +```dart +String GUID = "cometchat-guid-11"; +String groupName = "Hello Group!"; +String groupType = CometChatGroupType.public; + +Group group = Group(guid: GUID, name: groupName, type: groupType); +List members = [ + GroupMember(uid: "cometchat-uid-1", scope: GroupMemberScope.participant), +]; +List bannedUserIds = ["cometchat-uid-2"]; + +CometChat.createGroupWithMembers( + group: group, + groupMembers: members, + bannedUserIds: bannedUserIds, + onSuccess: (Group group) { + debugPrint("Group created with members: ${group.name}"); + }, + onError: (CometChatException e) { + debugPrint("Error creating group with members: ${e.message}"); + }, +); +``` + + + + + + +**On Success** — A `Group` object containing all details of the newly created group: + + + +**Group Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `guid` | string | Unique identifier for the group | `"cometchat-guid-11"` | +| `name` | string | Display name of the group | `"Hello Group!"` | +| `icon` | string | URL of the group icon | `null` | +| `description` | string | Description of the group | `null` | +| `membersCount` | number | Number of members in the group | `2` | +| `metadata` | object | Custom metadata attached to the group | `{}` | +| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | +| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | +| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | +| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | +| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | +| `tags` | array | List of tags associated with the group | `[]` | +| `type` | string | Type of the group (public, private, password) | `"public"` | +| `scope` | string | Scope of the logged-in user in the group | `"admin"` | +| `password` | string | Password for password-protected groups | `null` | +| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified group does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | + + + +## Group Class + +The [`Group`](/sdk/reference/entities#group) object has the following fields. Fields marked "Yes" in the Editable column can be modified after creation using `updateGroup()`. + +| Field | Editable | Information | +| ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------- | +| guid | Needs to be specified at group creation. Cannot be edited later | A unique identifier for a group | +| name | Yes | Name of the group | +| type | No | Type of the group: Can be 1. Public 2. Password 3. Private | +| password | No | Password for the group in case the group is of type password. | +| icon | Yes | An URL to group icon | +| description | Yes | Description about the group | +| owner | Yes | UID of the owner of the group. | +| metadata | Yes | Additional data for the group as JSON | +| createdAt | No | The unix timestamp of the time the group was created | +| updatedAt | No | The unix timestamp of the time the group was last updated | +| hasJoined | No | A boolean to determine if the logged in user is a member of the group. | +| joinedAt | No | The unix timestamp of the time the logged in user joined the group. | +| scope | Yes | Scope of the logged in user. Can be: 1. Admin 2. Moderator 3. Participant | +| membersCount | No | The number of members in the groups | +| tags | Yes | A list of tags to identify specific groups. | + +--- + +## Next Steps + + + + Join public, private, or password-protected groups + + + Add users to an existing group + + + Fetch and filter group lists + + + Overview of all group management features + + diff --git a/sdk/flutter/v4/delete-conversation.mdx b/sdk/flutter/v4/delete-conversation.mdx new file mode 100644 index 000000000..7eed0b031 --- /dev/null +++ b/sdk/flutter/v4/delete-conversation.mdx @@ -0,0 +1,125 @@ +--- +title: "Delete Conversation" +sidebarTitle: "Delete Conversation" +description: "Learn how to delete user and group conversations from the logged-in user's conversation list using the CometChat Flutter SDK." +--- + +{/* TL;DR for Agents and Quick Reference */} + + +```dart +// Delete a user conversation +await CometChat.deleteConversation( + "UID", + CometChatConversationType.user, + onSuccess: (String message) => debugPrint("Deleted: $message"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Delete a group conversation +await CometChat.deleteConversation( + "GUID", + CometChatConversationType.group, + onSuccess: (String message) => debugPrint("Deleted: $message"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); +``` + +**Note:** Deletes only for the logged-in user. Use [REST API](https://api-explorer.cometchat.com/reference/deletes-conversation) to delete for all participants. + + + +This operation is irreversible. Deleted conversations cannot be recovered. All messages in the conversation will be removed from the user's view. + + +Use `deleteConversation()` to delete a conversation for the logged-in user. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +This method takes two parameters: the unique id (`UID`/`GUID`) of the conversation to be deleted and the type (`user`/`group`) of conversation to be deleted. + + + +```dart +String conversationWith = "UID"; +String conversationType = CometChatConversationType.user; +await CometChat.deleteConversation(conversationWith, conversationType, + onSuccess: (String str){ + debugPrint("Conversation deleted successfully at : $str"); + }, + onError: (CometChatException e){ + debugPrint("Conversation deletion failed : ${e.message}"); + } +); +``` + + + + +```dart +String conversationWith = "GUID"; +String conversationType = CometChatConversationType.group; +await CometChat.deleteConversation(conversationWith, conversationType, + onSuccess: (String str){ + debugPrint("Conversation deleted successfully at : $str"); + }, + onError: (CometChatException e){ + debugPrint("Conversation deletion failed : ${e.message}"); + } +); +``` + + + + + +This method deletes the conversation only for the logged-in user. To delete a conversation for all the users of the conversation, please refer to the [REST API documentation](https://api-explorer.cometchat.com/reference/deletes-conversation). + +| Parameter | Type | Description | Required | +| --- | --- | --- | --- | +| `conversationWith` | `String` | `UID` of the user or `GUID` of the group whose conversation you want to delete. | Yes | +| `conversationType` | `String` | The type of conversation to delete. Use `CometChatConversationType.user` or `CometChatConversationType.group`. | Yes | + + +**On Success** — A `String` message confirming the conversation was deleted: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"cometchat-uid-1_user_cometchat-uid-2 deleted successfully"` | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to delete the conversation."` | +| `details` | string | Additional technical details | `"The specified conversation could not be found or deleted."` | + + + +--- + +## Next Steps + + + + Fetch and filter conversation lists + + + Delete individual messages from conversations + + + Listen for incoming messages in real-time + + + Show when users are typing in conversations + + diff --git a/sdk/flutter/v4/delete-group.mdx b/sdk/flutter/v4/delete-group.mdx new file mode 100644 index 000000000..4f48f7e14 --- /dev/null +++ b/sdk/flutter/v4/delete-group.mdx @@ -0,0 +1,104 @@ +--- +title: "Delete A Group" +sidebarTitle: "Delete Group" +description: "Delete a group permanently using the CometChat Flutter SDK. Only the group owner can perform this operation." +--- + + + +```dart +// Delete a group (owner only) +String guid = "GROUP_ID"; + +CometChat.deleteGroup(guid, + onSuccess: (String message) { + debugPrint("Deleted: $message"); + }, + onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); + }, +); +``` + +**Requirement:** Logged-in user must be the owner of the group. + + +Permanently delete a group and all its messages. Only the group owner can perform this operation. The group is represented by a [`Group`](/sdk/reference/entities#group) object. + + +This operation is irreversible. Deleted groups and their messages cannot be recovered. + + +## Delete a Group + +Use `deleteGroup()` with the group's GUID. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `guid` | `String` | The GUID of the group you would like to delete | +| `onSuccess` | `Function(String message)?` | Callback triggered on successful deletion | +| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | + + + +```dart +String GUID = "GUID"; + +CometChat.deleteGroup(GUID, onSuccess: (String message) { +debugPrint("Deleted Group Successfully : $message "); +}, onError: (CometChatException e) { +debugPrint("Delete Group failed with exception: ${e.message}"); +}); +``` + + + + + + +**On Success** — A `String` message confirming the operation: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"cometchat-guid-1 deleted successfully"` | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified group does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid group GUID."` | + + + +--- + +## Next Steps + + + + Update group name, icon, description, and metadata + + + Leave a group you are a member of + + + Create public, private, or password-protected groups + + + Overview of all group management features + + diff --git a/sdk/flutter/v4/delete-message.mdx b/sdk/flutter/v4/delete-message.mdx new file mode 100644 index 000000000..0240e6336 --- /dev/null +++ b/sdk/flutter/v4/delete-message.mdx @@ -0,0 +1,272 @@ +--- +title: "Delete A Message" +sidebarTitle: "Delete Message" +description: "Delete messages and handle real-time deletion events using the CometChat Flutter SDK." +--- + +{/* TL;DR for Agents and Quick Reference */} + + +| Field | Value | +| --- | --- | +| Key Classes | `BaseMessage`, `CometChatException` | +| Key Methods | `CometChat.deleteMessage()` | +| Listener Events | `onMessageDeleted` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Delete a message by ID +int messageId = 1234; +await CometChat.deleteMessage(messageId, + onSuccess: (BaseMessage message) { + debugPrint("Message deleted at: ${message.deletedAt}"); + }, + onError: (CometChatException e) { + debugPrint("Delete failed: ${e.message}"); + }, +); + +// Listen for deleted messages +CometChat.addMessageListener("listener_id", MessageListener( + onMessageDeleted: (BaseMessage message) { + debugPrint("Message ${message.id} was deleted"); + }, +)); + +// Remove listener when done +CometChat.removeMessageListener("listener_id"); +``` + +**Who can delete:** Message sender, Group admin, Group moderator +**Deleted fields:** `deletedAt` (timestamp), `deletedBy` (user who deleted) + + + +This operation is irreversible. Deleted messages cannot be recovered. + + +While [deleting a message](/sdk/flutter/delete-message#delete-a-message) is straightforward, receiving events for deleted messages with CometChat has two parts: + +1. Adding a listener to receive [real-time message deletes](/sdk/flutter/delete-message#real-time-message-delete-events) when your app is running. +2. Calling a method to retrieve [missed message delete events](/sdk/flutter/delete-message#missed-message-delete-events) when your app was not running. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Delete a Message + +*In other words, as a sender, how do I delete a message?* + +Use `deleteMessage()` with the message ID of the message to be deleted. + +| Parameter | Type | Description | +| --- | --- | --- | +| `messageId` | `int` | The ID of the message to delete. | +| `onSuccess` | `Function(BaseMessage)` | Callback triggered on success with the deleted message object. | +| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | + + + +```dart +int messageId=1234; + +await CometChat.deleteMessage(messageId, + onSuccess: (BaseMessage message){ + debugPrint("Message deleted successfully at : $message"); + }, onError: (CometChatException e){ + debugPrint("Message deletion failed : ${e.message}"); + } +); +``` + + + + + + +**On Success** — A `BaseMessage` object with `deletedAt` and `deletedBy` fields set: + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `1234` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#delete-message-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `"cometchat-uid-1"` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554750` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `1745554800` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#delete-message-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554800` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"The message could not be deleted."` | +| `details` | string | Additional technical details | `"Ensure the message ID is valid and you have permission to delete this message."` | + + + +The deleted message object is returned with `deletedAt` (timestamp) and `deletedBy` (UID of deleter) fields set. + +Relevant fields to access on the returned message: + +| Field | Property | Return Type | Description | +|-------|----------|-------------|-------------| +| deletedAt | `deletedAt` | `int` | Timestamp when the message was deleted | +| deletedBy | `deletedBy` | `String` | UID of the user who deleted the message | + +By default, CometChat allows certain roles to delete a message. + +| User Role | Conversation Type | Deletion Capabilities | +| --------------- | ----------------------- | ------------------------- | +| Message Sender | One-on-one Conversation | Messages they've sent | +| Message Sender | Group Conversation | Messages they've sent | +| Group Admin | Group Conversation | All messages in the group | +| Group Moderator | Group Conversation | All messages in the group | + +## Real-time Message Delete Events + +*In other words, as a recipient, how do I know when someone deletes a message when my app is running?* + +Use `onMessageDeleted` in `MessageListener` to receive real-time delete events. + + + +```dart +class Class_Name with MessageListener { + +//CometChat.addMessageListener("listenerId", this); +@override +void onMessageDeleted(BaseMessage message) { + // TODO implement onMessageDeleted +} + + +} +``` + + + + + + +Always remove message listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +@override +void dispose() { + CometChat.removeMessageListener("listenerId"); + super.dispose(); +} +``` + + +The `onMessageDeleted` callback receives a [`BaseMessage`](/sdk/reference/messages#basemessage) object with the `deletedAt` and `deletedBy` fields set. + +Relevant fields to access on the returned message: + +| Field | Property | Return Type | Description | +|-------|----------|-------------|-------------| +| deletedAt | `deletedAt` | `int` | Timestamp when the message was deleted | +| deletedBy | `deletedBy` | `String` | UID of the user who deleted the message | + +## Missed Message Delete Events + +*In other words, as a recipient, how do I know if someone deleted a message when my app was not running?* + +When you retrieve the list of previous messages, for the messages that were deleted, the `deletedAt` and the `deletedBy` fields will be set. Also, for example, if the total number of messages for a conversation are 100, and the message with message ID 50 was deleted. Now the message with ID 50 will have the `deletedAt` and the `deletedBy` fields set whenever it is pulled from the history. Also, the 101st message will be an `Action` message informing you that the message with ID 50 has been deleted. + +For the message deleted event, in the `Action` object received, the following fields can help you get the relevant information- + +1. `action` - `deleted` +2. `actionOn` - Updated message object which was deleted. +3. `actionBy` - User object containing the details of the user who has deleted the message. +4. `actionFor` - User/group object having the details of the receiver to which the message was sent. + + +You must be the message sender or a group admin/moderator to delete a message. + + +--- + +## Next Steps + + + + Modify sent messages before deletion + + + Send text, media, and custom messages + + + Handle incoming messages in real-time + + + Report inappropriate messages + + diff --git a/sdk/flutter/v4/delivery-read-receipts.mdx b/sdk/flutter/v4/delivery-read-receipts.mdx new file mode 100644 index 000000000..ca2605290 --- /dev/null +++ b/sdk/flutter/v4/delivery-read-receipts.mdx @@ -0,0 +1,418 @@ +--- +title: "Delivery & Read Receipts" +sidebarTitle: "Delivery & Read Receipts" +description: "Mark messages as delivered, read, or unread and receive real-time receipt events using the CometChat Flutter SDK." +--- + + + +| Field | Value | +| --- | --- | +| Key Classes | `MessageReceipt`, `BaseMessage`, `Conversation` | +| Key Methods | `CometChat.markAsDelivered()`, `CometChat.markAsRead()`, `CometChat.markMessageAsUnread()`, `CometChat.getMessageReceipts()` | +| Listener Events | `onMessagesDelivered`, `onMessagesRead`, `onMessagesDeliveredToAll`, `onMessagesReadByAll` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Mark message as delivered +CometChat.markAsDelivered(message, onSuccess: (String unused) { + debugPrint("Marked as delivered"); +}, onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); +}); + +// Mark message as read +CometChat.markAsRead(message, onSuccess: (String unused) { + debugPrint("Marked as read"); +}, onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); +}); + +// Mark message as unread +CometChat.markMessageAsUnread(message, onSuccess: (Conversation conversation) { + debugPrint("Marked as unread"); +}, onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); +}); + +// Listen for receipts +CometChat.addMessageListener("receipts_listener", MessageListener( + onMessagesDelivered: (MessageReceipt receipt) { }, + onMessagesRead: (MessageReceipt receipt) { }, + onMessagesDeliveredToAll: (MessageReceipt receipt) { }, + onMessagesReadByAll: (MessageReceipt receipt) { }, +)); + +// Remove listener when done +CometChat.removeMessageListener("receipts_listener"); +``` + + + +Delivery and read receipts allow you to track when messages have been delivered to and read by recipients, providing real-time feedback on message status. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Mark Messages as Delivered + +*In other words, as a recipient, how do I inform the sender that I've received a message?* + +You can mark the messages for a particular conversation as delivered using the `markAsDelivered()` method. This method takes a `BaseMessage` object as input. + + +| Parameter | Type | Description | +| --- | --- | --- | +| `message` | [`BaseMessage`](/sdk/reference/messages#basemessage) | The message object to mark as delivered. All messages before this message in the conversation will be marked as delivered. | +| `onSuccess` | `Function(String)` | Callback triggered on success with a confirmation string. | +| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | + +Messages for both user & group conversations can be marked as delivered using this method. + +Ideally, you would like to mark all the messages as delivered for any conversation when the user opens the chat window for that conversation. This includes two scenarios: + +1. **When the list of messages for the conversation is fetched**: In this case you need to obtain the last message in the list of messages and pass the message to the `markAsDelivered()` method. +2. **When the user is on the chat window and a real-time message is received:** In this case you need to obtain the message and pass it to the `markAsDelivered()` method. + +This method will mark all the messages before the message specified, for the conversation with `receiverId` and `receiverType` (user/group) as delivered. + +In case you would like to be notified of an error if the receipts fail to go through you can use `markAsDelivered()` method with the callbacks as shown below: + + + +```dart +CometChat.markAsDelivered(message, onSuccess: (String unused) { + debugPrint("markAsDelivered : $unused "); +}, onError: (CometChatException e) { + debugPrint("markAsDelivered unsuccessful : ${e.message} "); +}); +``` + + + + + + +**On Success** — A `String` message confirming the message has been marked as delivered: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | `String` | Success confirmation message | `"markAsDelivered success"` | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | `String` | Human-readable error message | `"Failed to mark the message as delivered."` | +| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Mark Messages as Read + +*In other words, as a recipient, how do I inform the sender I've read a message?* + +You can mark the messages for a particular conversation as read using the `markAsRead()` method. This method takes a `BaseMessage` object as input. + +| Parameter | Type | Description | +| --- | --- | --- | +| `baseMessage` | [`BaseMessage`](/sdk/reference/messages#basemessage) | The message object to mark as read. All messages before this message in the conversation will be marked as read. | +| `onSuccess` | `Function(String)` | Callback triggered on success with a confirmation string. | +| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | + +Messages for both user and group conversations can be marked as read using this method. + +Ideally, you should mark all the messages as read for any conversation when the user opens the chat window for that conversation. This includes two scenarios: + +1. **When the list of messages for the conversation is fetched**: In this case you need to obtain the last message in the list of messages and pass the message to the `markAsRead()` method. +2. **When the user is on the chat window and a real-time message is received:** In this case you need to obtain the message and pass it to the `markAsRead()` method + +This method will mark all the messages before the message specified, for the conversation with `receiverId` and `receiverType` (user/group) as read. + +Another option the CometChat SDK provides is to pass the entire message object to the markAsRead() method. If the message object is the last message, the entire conversation will be marked as read. + + + +```dart +CometChat.markAsRead(message) +``` + + + + + +In case you would like to be notified of an error if the receipts fail to go through you can use the `markAsRead()` method with the callbacks as shown below: + + + +```dart +CometChat.markAsRead(message, onSuccess: (String unused) { + debugPrint("markAsRead : $unused "); + reinitiateList(); + }, onError: (CometChatException e) { + debugPrint("markAsRead unsuccessfull : ${e.message} "); + }); +``` + + + + + + +**On Success** — A `String` message confirming the message has been marked as read: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | `String` | Success confirmation message | `"markAsRead success"` | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | `String` | Human-readable error message | `"Failed to mark the message as read."` | +| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Mark Messages as Unread + +The Mark as Unread feature allows users to designate specific messages or conversations as unread, even if they have been previously viewed. + +This feature is valuable for users who want to revisit and respond to important messages or conversations later, ensuring they don't forget or overlook them. + +*In other words, how I can mark message as unread?* + +You can mark the messages for a particular conversation as unread using the `markMessageAsUnread()` method. + +| Parameter | Type | Description | +| --- | --- | --- | +| `baseMessage` | [`BaseMessage`](/sdk/reference/messages#basemessage) | To mark a message as unread, pass a non-null `BaseMessage` instance. All messages below that message in the conversation will contribute to the unread messages count. | +| `onSuccess` | `Function(Conversation)` | Callback triggered on success with the updated `Conversation` object containing the new unread count. | +| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | + +Example: When User B sends User A a total of 10 messages, and User A invokes the `markMessageAsUnread()` method on the fifth message, all messages located below the fifth message within the conversation list will be designated as unread. This results in a notification indicating there are 5 unread messages in the conversation list. + + + +```dart +CometChat.markMessageAsUnread( + message, + onSuccess: (Conversation conversation) { + debugPrint("markMessageAsUnread : $conversation"); + }, + onError: (CometChatException error) { + debugPrint("markMessageAsUnread unsuccessful : $error"); + }, +); +``` + + + + + + +**On Success** — A `Conversation` object with the updated unread message count: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `conversationId` | `String` | Unique identifier for the conversation | `"user_superhero1"` | +| `conversationType` | `String` | Type of conversation | `"user"` or `"group"` | +| `unreadMessageCount` | `int` | Updated unread message count | `5` | +| `lastMessage` | `BaseMessage` | The last message in the conversation | Message object | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | `String` | Human-readable error message | `"Failed to mark the message as unread."` | +| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Receive Delivery & Read Receipts + +*In other words, as a recipient, how do I know when a message I sent has been delivered or read by someone?* + +### Real-time Events + +Register a `MessageListener` to receive delivery and read receipt events. + +| Callback | Description | +| --- | --- | +| `onMessagesDelivered` | Message delivered to a user | +| `onMessagesRead` | Message read by a user | +| `onMessagesDeliveredToAll` | Group message delivered to all members | +| `onMessagesReadByAll` | Group message read by all members | + + + +```dart +class Class_Name with MessageListener { + +//CometChat.addMessageListener("listenerId", this); +@override +void onMessagesDelivered(MessageReceipt messageReceipt) { + // TODO: implement onMessagesDelivered +} + +@override +void onMessagesRead(MessageReceipt messageReceipt) { + // TODO: implement onMessagesRead +} + +@override +void onMessagesDeliveredToAll(MessageReceipt messageReceipt) { + // TODO: implement onMessagesDeliveredToAll +} + +@override +void onMessagesReadByAll(MessageReceipt messageReceipt) { + // TODO: implement onMessagesReadByAll +} + +} +``` + + + + + + +Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +@override +void dispose() { + CometChat.removeMessageListener("listenerId"); + super.dispose(); +} +``` + + +You will receive events in the form of [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) objects. The message receipt contains the following parameters: + +| Parameter | Type | Description | +| -------------- | ---- | -------------------------------------------------------------------------------------------------------------------- | +| `messageId` | `int` | The ID of the message prior to which all the messages for that particular conversation have been marked as read. | +| `sender` | [`User`](/sdk/reference/entities#user) | User object containing the details of the user who has marked the message as read. | +| `receiverId` | `String` | Id of the receiver whose conversation has been marked as read. | +| `receiverType` | `String` | Type of the receiver (user/group) | +| `receiptType` | `String` | Type of the receipt (read/delivered) | +| `deliveredAt` | `DateTime` | The timestamp of the time when the message was delivered. This will only be present if the receiptType is delivered. | +| `readAt` | `DateTime` | The timestamp of the time when the message was read. This will only be present when the receiptType is read. | + +### Missed Receipts + +You will receive message receipts when you load offline messages. While fetching messages in bulk, the message object will have two fields i.e. `deliveredAt` and `readAt` which hold the timestamp for the time the message was delivered and read respectively. Using these two variables, the delivery and read status for a message can be obtained. + +However, for a group message, if you wish to fetch the `deliveredAt` and `readAt` fields of individual member of the group you can use the below-described method. + +## Receipt History for a Single Message + +To fetch the message receipts, you can use the `getMessageReceipts()` method. This is useful for group messages to see which members have received/read the message. + +| Parameter | Type | Description | +| --- | --- | --- | +| `messageId` | `int` | The ID of the message for which receipts are to be fetched. | +| `onSuccess` | `Function(List)` | Callback triggered on success with a list of `MessageReceipt` objects. | +| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | + + + +```dart +int messageId = 10101; + +CometChat.getMessageReceipts(messageId, onSuccess: (List messageReceipts) { + debugPrint("Message receipts fetched: $messageReceipts"); +}, onError: (CometChatException e) { + debugPrint("Error fetching receipts: ${e.message}"); +}); +``` + + + + + + +**On Success** — A `List` containing receipt details for each group member: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `messageId` | `int` | The ID of the message | `10101` | +| `sender` | `User` | User who triggered the receipt | User object | +| `receiverId` | `String` | ID of the receiver | `"superhero1"` | +| `receiverType` | `String` | Type of receiver | `"user"` or `"group"` | +| `receiptType` | `String` | Type of receipt | `"delivered"` or `"read"` | +| `deliveredAt` | `DateTime` | Timestamp when delivered | `DateTime` object | +| `readAt` | `DateTime` | Timestamp when read | `DateTime` object | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | `String` | Human-readable error message | `"Failed to fetch message receipts."` | +| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +You will receive a list of `MessageReceipt` objects in the `onSuccess()` callback. + + + +The following features will be available only if the **Enhanced Messaging Status** feature is enabled for your app. + +* `onMessagesDeliveredToAll` event, +* `onMessagesReadByAll` event, +* `deliveredAt` field in a group message, +* `readAt` field in a group message. +* `markMessageAsUnread` method. + + + +--- + +## Next Steps + + + + Handle incoming messages in real-time + + + Show when users are typing + + + Fetch conversation list with unread counts + + + Complete reference for all SDK event listeners + + diff --git a/sdk/flutter/v4/edit-message.mdx b/sdk/flutter/v4/edit-message.mdx new file mode 100644 index 000000000..f9f54efde --- /dev/null +++ b/sdk/flutter/v4/edit-message.mdx @@ -0,0 +1,296 @@ +--- +title: "Edit Message" +sidebarTitle: "Edit Message" +description: "Edit text and custom messages using the CometChat Flutter SDK." +--- + +{/* TL;DR for Agents and Quick Reference */} + + +| Field | Value | +| --- | --- | +| Key Classes | `TextMessage`, `CustomMessage`, `BaseMessage` | +| Key Methods | `CometChat.editMessage()` | +| Listener Events | `onMessageEdited` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Edit a text message +TextMessage updatedMessage = TextMessage( + text: "Updated message text", + receiverUid: "receiver_uid", + receiverType: CometChatReceiverType.user, + type: CometChatMessageType.text, +); +updatedMessage.id = originalMessageId; + +await CometChat.editMessage(updatedMessage, + onSuccess: (BaseMessage message) { + debugPrint("Message edited: $message"); + }, + onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); + }, +); + +// Listen for real-time edits +CometChat.addMessageListener("edits_listener", MessageListener( + onMessageEdited: (BaseMessage message) { + debugPrint("Message was edited: ${message.id}"); + }, +)); + +// Remove listener when done +CometChat.removeMessageListener("edits_listener"); +``` + + + +Editing a message is straightforward. Receiving edit events has two parts: + +1. Adding a listener for [real-time edits](#real-time-message-edit-events) when your app is running +2. Fetching [missed edits](#missed-message-edit-events) when your app was offline + +## Edit a Message + +Use `editMessage()` with a [`TextMessage`](/sdk/reference/messages#textmessage) or [`CustomMessage`](/sdk/reference/messages#custommessage) object. Set the message ID using the `id` property. + +| Parameter | Type | Description | +| --- | --- | --- | +| `message` | `BaseMessage` | The message object to edit. Must be a `TextMessage` or `CustomMessage`. Set the `id` property to the ID of the message to edit. | +| `onSuccess` | `Function(BaseMessage)` | Callback triggered on success with the edited message object. | +| `onError` | `Function(CometChatException)` | Callback triggered on error with exception details. | + +### Add/Update Tags + +Use `tags` to update tags when editing. New tags replace existing ones. + + + +```dart +List tags = []; +tags.add("pinned"); +textMessage.tags = tags; +``` + + + + +```dart +List tags = []; +tags.add("pinned"); +customMessage.tags = tags; +``` + + + + + +Once the message object is ready, call `editMessage()`. + + + +```dart +TextMessage updatedMessage = TextMessage( + text: updatedText, + receiverUid: receiverID, + receiverType: receiverType, + type: type); + +updatedMessage.id = message.id; + +await CometChat.editMessage(updatedMessage, + onSuccess: (BaseMessage message){ + debugPrint("Message Edited successfully: $message"); + }, onError: (CometChatException e){ + debugPrint("Message Edited failed with exception: ${e.message}"); + } +); +``` + + + + + + +**On Success** — A `BaseMessage` object containing all details of the edited message: + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `401` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#edit-message-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `"cometchat-uid-1"` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554750` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#edit-message-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `1745554800` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554800` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"The message could not be modified."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +The edited message object is returned with `editedAt` (timestamp) and `editedBy` (UID of editor) fields set. + +The `editMessage()` method returns a [`BaseMessage`](/sdk/reference/messages#basemessage) object (or a subclass like [`TextMessage`](/sdk/reference/messages#textmessage)). + +Relevant fields to access on the returned message: + +| Field | Property | Return Type | Description | +|-------|----------|-------------|-------------| +| editedAt | `editedAt` | `int` | Timestamp when the message was edited | +| editedBy | `editedBy` | `String` | UID of the user who edited the message | + +By default, CometChat allows certain roles to edit a message. + +| User Role | Conversation Type | Edit Capabilities | +| --------------- | ----------------------- | ------------------------- | +| Message Sender | One-on-one Conversation | Messages they've sent | +| Message Sender | Group Conversation | Messages they've sent | +| Group Owner | Group Conversation | All messages in the group | +| Group Moderator | Group Conversation | All messages in the group | + +## Real-time Message Edit Events + +Use `onMessageEdited` in `MessageListener` to receive real-time edit events. + + + +```dart +class Class_Name with MessageListener { + +//CometChat.addMessageListener("listenerId", this); +@override +void onMessageEdited(BaseMessage message) { + // TODO: implement onMessageEdited +} + + +} +``` + + + + + +Always remove message listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +@override +void dispose() { + CometChat.removeMessageListener("listenerId"); + super.dispose(); +} +``` + + +The `onMessageEdited` callback receives a [`BaseMessage`](/sdk/reference/messages#basemessage) object with the `editedAt` and `editedBy` fields set. + +Relevant fields to access on the returned message: + +| Field | Property | Return Type | Description | +|-------|----------|-------------|-------------| +| editedAt | `editedAt` | `int` | Timestamp when the message was edited | +| editedBy | `editedBy` | `String` | UID of the user who edited the message | + +## Missed Message Edit Events + +When fetching message history, edited messages have `editedAt` and `editedBy` fields set. Additionally, an [`Action`](/sdk/reference/messages#action) message is created when a message is edited. + +The [`Action`](/sdk/reference/messages#action) object contains: +- `action` — `edited` +- `actionOn` — Updated message object with the edited details +- `actionBy` — User object containing the details of the user who has edited the message +- `actionFor` — User/group object having the details of the receiver to which the message was sent + + +You must be the message sender or a group admin/moderator to edit a message. + + +--- + +## Next Steps + + + + Learn how to delete messages from conversations + + + Send text, media, and custom messages + + + Handle incoming messages in real-time + + + Create and manage message threads + + diff --git a/sdk/flutter/error-codes.mdx b/sdk/flutter/v4/error-codes.mdx similarity index 100% rename from sdk/flutter/error-codes.mdx rename to sdk/flutter/v4/error-codes.mdx diff --git a/sdk/flutter/v4/extensions-overview.mdx b/sdk/flutter/v4/extensions-overview.mdx new file mode 100644 index 000000000..5dcf22fb9 --- /dev/null +++ b/sdk/flutter/v4/extensions-overview.mdx @@ -0,0 +1,144 @@ +--- +title: "Extensions" +sidebarTitle: "Extensions" +description: "Explore CometChat extensions that add enhanced functionality to your Flutter chat application" +url: "/fundamentals/extensions-overview" +--- + + +**What are Extensions?** +Extensions are add-on features that extend CometChat's core functionality beyond basic messaging. + +**How to Enable:** +1. Go to [CometChat Dashboard](https://app.cometchat.com) +2. Navigate to your App → Extensions +3. Enable desired extensions +4. Extensions activate automatically on SDK initialization + +**Extension Categories:** +- **User Experience** → Pin message, Link preview, Thumbnails, Voice transcription +- **User Engagement** → Polls, Reactions, Mentions, Message translation, Stickers +- **Collaboration** → Whiteboard, Collaborative documents +- **Notifications** → Push, Email, SMS notifications +- **Moderation** → Content filtering, Profanity detection +- **Security** → Disappearing messages, End-to-end encryption + +**Full Extension List:** [Extensions Overview](/fundamentals/extensions-overview) + + +Extensions extend CometChat's core functionality, adding enhanced features to your chat application. They help you build a complete chat experience beyond basic voice, video, and text messaging. + +## How Extensions Work + +Extensions are enabled through the CometChat Dashboard and automatically integrate with your Flutter application upon SDK initialization and successful login. Once enabled, extension features become available without additional SDK configuration. + +## Enabling Extensions + + + + Log in to the [CometChat Dashboard](https://app.cometchat.com) and select your application. + + + Go to the Extensions section in your app settings. + + + Toggle on the extensions you want to use in your application. + + + Extensions activate automatically when your Flutter app initializes the CometChat SDK. + + + +## Available Extension Categories + +### User Experience + +Extensions that improve the messaging experience: + +| Extension | Description | +| --- | --- | +| Pin Message | Allow users to pin important messages | +| Link Preview | Automatically generate previews for shared links | +| Rich Media Preview | Enhanced media previews for images and videos | +| Thumbnail Generation | Auto-generate thumbnails for media files | +| Voice Transcription | Convert voice messages to text | +| Save Message | Let users bookmark messages for later | + +### User Engagement + +Extensions that increase user interaction: + +| Extension | Description | +| --- | --- | +| Polls | Create and vote on polls within chats | +| Reactions | Add emoji reactions to messages | +| Mentions | Tag users in messages with @mentions | +| Message Translation | Translate messages to different languages | +| Smart Reply | AI-powered reply suggestions | +| Stickers | Send stickers in conversations | +| Giphy/Tenor | Search and share GIFs | + +### Collaboration + +Extensions for team collaboration: + +| Extension | Description | +| --- | --- | +| Collaborative Whiteboard | Real-time whiteboard for visual collaboration | +| Collaborative Document | Edit documents together in real-time | + +### Notifications + +Extensions for alerting users: + +| Extension | Description | +| --- | --- | +| Push Notifications | Mobile and web push alerts | +| Email Notifications | Email alerts for missed messages | +| SMS Notifications | Text message alerts | + +### Moderation + +Extensions for content safety: + +| Extension | Description | +| --- | --- | +| Profanity Filter | Automatically filter inappropriate content | +| Image Moderation | Detect and filter inappropriate images | +| Data Masking | Mask sensitive information in messages | + +### Security + +Extensions for enhanced security: + +| Extension | Description | +| --- | --- | +| Disappearing Messages | Messages that auto-delete after a set time | +| End-to-End Encryption | Secure message encryption | + +## Extension Support in UI Kit + +If you're using CometChat UI Kit for Flutter, many extensions are automatically supported and rendered in the UI. Extension features will only be available if they are supported by the CometChat UI Kit. + + +For detailed information on each extension, including configuration options and usage, visit the [Extensions Overview](/fundamentals/extensions-overview) in the fundamentals documentation. + + +--- + +## Next Steps + + + + Explore all available SDK resources and documentation + + + Configure webhooks for real-time event notifications + + + Understand API rate limits and quotas + + + Handle real-time events in your application + + diff --git a/sdk/flutter/v5/flag-message.mdx b/sdk/flutter/v4/flag-message.mdx similarity index 54% rename from sdk/flutter/v5/flag-message.mdx rename to sdk/flutter/v4/flag-message.mdx index 02b71550c..a2f8d6530 100644 --- a/sdk/flutter/v5/flag-message.mdx +++ b/sdk/flutter/v4/flag-message.mdx @@ -1,11 +1,48 @@ --- title: "Flag Message" +sidebarTitle: "Flag Message" +description: "Learn how to flag and report inappropriate messages for moderation review in your Flutter application using CometChat SDK." --- +{/* TL;DR for Agents and Quick Reference */} + + +```dart +// Get available flag reasons +CometChat.getFlagReasons( + onSuccess: (List reasons) { + debugPrint("Flag reasons: $reasons"); + }, + onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); + }, +); + +// Flag a message +FlagDetail flagDetail = FlagDetail( + reasonId: "spam", + remark: "Optional additional context", +); + +CometChat.flagMessage(messageId, flagDetail, + onSuccess: (String response) { + debugPrint("Message flagged: $response"); + }, + onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); + }, +); +``` + + ## Overview Flagging messages allows users to report inappropriate content to moderators or administrators. When a message is flagged, it appears in the [CometChat Dashboard](https://app.cometchat.com) under **Moderation > Flagged Messages** for review. + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [Dashboard](https://app.cometchat.com) + + For a complete understanding of how flagged messages are reviewed and managed, see the [Flagged Messages](/moderation/flagged-messages) documentation. @@ -51,7 +88,7 @@ Before flagging a message, retrieve the list of available flag reasons configure print("Flag reasons fetched: $reasons"); // Use reasons to populate your report dialog UI for (var reason in reasons) { - print("Reason ID: ${reason.id}, Title: ${reason.title}"); + print("Reason ID: ${reason.id}, Name: ${reason.name}"); } }, onError: (CometChatException e) { @@ -68,8 +105,21 @@ The response is a list of `FlagReason` objects containing: | Property | Type | Description | |----------|------|-------------| -| id | String | Unique identifier for the reason | -| title | String | Display text for the reason | +| `id` | `String` | Unique identifier for the reason | +| `name` | `String` | Display name for the reason | +| `description` | `String?` | Optional description of the reason | +| `createdAt` | `DateTime?` | Timestamp when the reason was created | +| `updatedAt` | `DateTime?` | Timestamp when the reason was last updated | + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | `String` | Human-readable error message | `"Failed to fetch flag reasons."` | +| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | + + ## Flag a Message @@ -80,9 +130,10 @@ To flag a message, use the `flagMessage()` method with the message ID and a `Fla ```dart int messageId = 123; // ID of the message to flag - FlagDetail flagDetail = FlagDetail() - ..reasonId = "spam" // Required: ID from getFlagReasons() - ..remark = "This message contains promotional content"; // Optional + FlagDetail flagDetail = FlagDetail( + reasonId: "spam", // Required: ID from getFlagReasons() + remark: "This message contains promotional content", // Optional + ); CometChat.flagMessage( messageId, @@ -102,18 +153,29 @@ To flag a message, use the `flagMessage()` method with the message ID and a `Fla | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| messageId | int | Yes | The ID of the message to flag | -| flagDetail | FlagDetail | Yes | Contains flagging details | -| flagDetail.reasonId | String | Yes | ID of the flag reason (from `getFlagReasons()`) | -| flagDetail.remark | String | No | Additional context or explanation from the user | +| `messageId` | `int` | Yes | The ID of the message to flag | +| `flagDetail` | `FlagDetail` | Yes | Contains flagging details | +| `flagDetail.reasonId` | `String` | Yes | ID of the flag reason (from `getFlagReasons()`) | +| `flagDetail.remark` | `String?` | No | Additional context or explanation from the user | -### Response + +**On Success** — A `String` message confirming the message has been flagged: -```json -{ - "message": "Message {id} has been flagged successfully." -} -``` +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | `String` | Success confirmation message | `"Message 123 has been flagged successfully."` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | `String` | Human-readable error message | `"Failed to flag the message."` | +| `details` | `String` | Additional technical details | `"An unexpected error occurred while processing the request."` | + + ## Complete Example @@ -154,12 +216,10 @@ Here's a complete implementation showing how to build a report message flow: }) async { final completer = Completer(); - FlagDetail flagDetail = FlagDetail() - ..reasonId = reasonId; - - if (remark != null) { - flagDetail.remark = remark; - } + FlagDetail flagDetail = FlagDetail( + reasonId: reasonId, + remark: remark, + ); CometChat.flagMessage( messageId, @@ -197,3 +257,22 @@ Here's a complete implementation showing how to build a report message flow: ``` + +--- + +## Next Steps + + + + Handle incoming messages and real-time events + + + Remove messages from conversations + + + Automate content moderation with AI + + + Track message delivery and read status + + diff --git a/sdk/flutter/v4/group-add-members.mdx b/sdk/flutter/v4/group-add-members.mdx new file mode 100644 index 000000000..36b38943c --- /dev/null +++ b/sdk/flutter/v4/group-add-members.mdx @@ -0,0 +1,178 @@ +--- +title: "Add Members To A Group" +sidebarTitle: "Add Members" +description: "Learn how to add members to a group, receive real-time member added events, and handle missed events using the CometChat Flutter SDK." +--- + + + +```dart +// Add members to a group +List members = [ + GroupMember.fromUid(uid: "UID", scope: CometChatMemberScope.participant, name: "User 1"), +]; + +CometChat.addMembersToGroup( + guid: "GUID", + groupMembers: members, + onSuccess: (Map result) { + debugPrint("Members added: $result"); + }, + onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); + }, +); + +// Listen for member added events +class MyGroupListener with GroupListener { + @override + void onMemberAddedToGroup(Action action, User addedby, User userAdded, Group addedTo) { + debugPrint("Member added: ${userAdded.name}"); + } +} +CometChat.addGroupListener("listenerId", MyGroupListener()); +``` + + +Add users to a group programmatically. Only admins and moderators can add members. The added users receive a notification and are immediately part of the group. + +## Add Members to Group + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +Use `addMembersToGroup()` to add members to a [Group](/sdk/flutter/retrieve-groups). + +| Parameter | Type | Description | +|-----------|------|-------------| +| `guid` | `String` | The GUID of the group to add members to | +| `groupMembers` | `List` | List of [GroupMember](/sdk/reference/entities#groupmember) objects (UID and scope required) | +| `onSuccess` | `Function(Map)` | Callback with a map of UIDs to result status | +| `onError` | `Function(CometChatException)` | Callback triggered on failure | + +In order to add members, you need to create an object of the `GroupMember` class. The `UID` and the `scope` of the `GroupMember` are mandatory. + + + +```dart +List groupMembers = []; +GroupMember firstMember = GroupMember.fromUid( +scope: CometChatMemberScope.participant, +uid: "cometchat-uid-3",name: "name"); +GroupMember secondMember = GroupMember.fromUid(scope: CometChatMemberScope.admin, +uid: "cometchat-uid-4",name: "name"); + +groupMembers = [firstMember, secondMember]; + +CometChat.addMembersToGroup( guid: conversationWith, + groupMembers: groupMembers, + onSuccess: (Map result) { + debugPrint("Group Member added Successfully : $result"); + }, + onError: (CometChatException e) { + debugPrint("Group Member addition failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `Map` where each key is the UID of the user and the value is either `"success"` or an error message: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `cometchat-uid-3` | string | Result for the first member added | `"success"` | +| `cometchat-uid-4` | string | Result for the second member added | `"success"` | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified group does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid group GUID."` | + + + +In the `onSuccess()` callback, you will receive a `Map` which will contain the `UID` of the users and the value will either be `success` or an error message describing why the operation to add the user to the group failed. + +## Real-Time Group Member Added Events + + +When a group member is added by another member, this event is triggered. When a user joins a group on their own, the joined event is triggered. + + +Implement `onMemberAddedToGroup()` in `GroupListener` to receive real-time notifications when members are added. + + + +```dart +class Class_Name with GroupListener { + +//CometChat.addGroupListener("group_Listener_id", this); +@override +void onMemberAddedToGroup(Action action, User addedby, User userAdded, Group addedTo) { + print("onMemberAddedToGroup"); +} +} +``` + + + + + +| Callback Parameter | Type | Description | +|--------------------|------|-------------| +| `action` | `Action` | The action object containing details of the event | +| `addedby` | `User` | The user who added the member | +| `userAdded` | `User` | The user who was added to the group | +| `addedTo` | `Group` | The group the member was added to | + + +Always remove group listeners when they're no longer needed (e.g., in `dispose()`). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +CometChat.removeGroupListener("group_Listener_id"); +``` + + +## Missed Member Added Events + +When you retrieve the list of previous messages, if a member has been added to any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. + +For the group member added event, in the `Action` object received, the following fields can help you get the relevant information: + +| Field | Value/Type | Description | +|-------|------------|-------------| +| `action` | `"added"` | The action type | +| `actionOn` | [User](/sdk/reference/entities#user) | The user who was added | +| `actionBy` | [User](/sdk/reference/entities#user) | The user who added the member | +| `actionFor` | [Group](/sdk/reference/entities#group) | The group the member was added to | + +--- + +## Next Steps + + + + Remove or ban members from a group + + + Promote or demote group members + + + Fetch the list of members in a group + + + Allow users to join public or password-protected groups + + diff --git a/sdk/flutter/v4/group-change-member-scope.mdx b/sdk/flutter/v4/group-change-member-scope.mdx new file mode 100644 index 000000000..0420f4fdb --- /dev/null +++ b/sdk/flutter/v4/group-change-member-scope.mdx @@ -0,0 +1,151 @@ +--- +title: "Change Member Scope" +sidebarTitle: "Change Scope" +description: "Learn how to change group member roles (admin, moderator, participant) and receive real-time scope change events using the CometChat Flutter SDK." +--- + + + +```dart +// Change member scope to admin +CometChat.updateGroupMemberScope( + guid: "GROUP_ID", + uid: "USER_ID", + scope: CometChatMemberScope.admin, + onSuccess: (String message) => debugPrint("Scope updated: $message"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Listen for scope change events +CometChat.addGroupListener("listener_id", GroupListenerImpl()); + +// Scopes: CometChatMemberScope.admin, .moderator, .participant +``` + + +Promote or demote group members between admin, moderator, and participant roles. Only admins can change member scopes, and only the group owner can change admin scopes. + +## Change Scope of a Group Member + +Use `updateGroupMemberScope()` to change a member's role within a [Group](/sdk/reference/entities#group). + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + + + +```dart +String UID = "UID"; +String GUID = "GUID"; +String scope = CometChatMemberScope.admin; + +CometChat.updateGroupMemberScope(guid: GUID, uid: UID, scope: scope, + onSuccess: (String message) { + debugPrint("Group Member Scope Changed Successfully : $message"); + }, + onError: (CometChatException e) { + debugPrint("Group Member Scope Change failed : ${e.message}"); + }); +``` + + + + + +This method takes the below parameters: + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `guid` | `String` | The GUID of the group for which the member's scope needs to be changed | +| `uid` | `String` | The UID of the member whose scope you would like to change | +| `scope` | `String` | The updated scope: `CometChatMemberScope.admin`, `CometChatMemberScope.moderator`, or `CometChatMemberScope.participant` | +| `onSuccess` | `Function(String)` | Callback triggered on successful scope change | +| `onError` | `Function(CometChatException)` | Callback triggered on error | + +The default scope of any member is `participant`. Only the **Admin** of the group can change the scope of any participant in the group. + + +**On Success** — A `String` message confirming the scope change: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"cometchat-guid-1 scope changed successfully"` | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_NOT_A_MEMBER"` | +| `message` | string | Human-readable error message | `"The user is not a member of this group."` | +| `details` | string | Additional technical details | `"Cannot change scope for a user who is not a member of the group."` | + + + +## Real-Time Group Member Scope Changed Events + +Implement `onGroupMemberScopeChanged()` in `GroupListener` to receive real-time notifications when a member's scope changes. + + + +```dart +class Class_Name with GroupListener { + +//CometChat.addGroupListener("group_Listener_id", this); +@override +void onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group) { + print("onGroupMemberScopeChanged "); + +} +} +``` + + + + + + +Always remove group listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +CometChat.removeGroupListener("group_Listener_id"); +``` + + +## Missed Group Member Scope Changed Events + +When fetching previous messages, scope changes appear as [Action](/sdk/reference/messages#action) messages (a subclass of `BaseMessage`). + +| Field | Value/Type | Description | +|-------|------------|-------------| +| `action` | `"scopeChanged"` | The action type | +| `actionOn` | [User](/sdk/reference/entities#user) | The user whose scope changed | +| `actionBy` | [User](/sdk/reference/entities#user) | The user who changed the scope | +| `actionFor` | [Group](/sdk/reference/entities#group) | The group | +| `oldScope` | `String` | The previous scope | +| `newScope` | `String` | The new scope | + +--- + +## Next Steps + + + + Transfer ownership of a group to another member + + + Remove or ban members from a group + + + Add new members to a group + + + Fetch the list of members in a group + + diff --git a/sdk/flutter/group-kick-ban-members.mdx b/sdk/flutter/v4/group-kick-ban-members.mdx similarity index 100% rename from sdk/flutter/group-kick-ban-members.mdx rename to sdk/flutter/v4/group-kick-ban-members.mdx diff --git a/sdk/flutter/v4/groups-overview.mdx b/sdk/flutter/v4/groups-overview.mdx new file mode 100644 index 000000000..3e27ebc8c --- /dev/null +++ b/sdk/flutter/v4/groups-overview.mdx @@ -0,0 +1,75 @@ +--- +title: "Groups" +sidebarTitle: "Overview" +description: "Overview of group management in the CometChat Flutter SDK including group types, member roles, and available operations." +--- + + + +| Field | Value | +| --- | --- | +| Package | `cometchat_sdk` | +| Key Classes | `CometChat`, `Group`, `GroupMember` | +| Group Types | `public`, `private`, `password` | +| Member Roles | `owner`, `admin`, `moderator`, `participant` | +| Key Methods | `createGroup()`, `joinGroup()`, `leaveGroup()`, `deleteGroup()` | +| Prerequisites | SDK initialized, user logged in | +| Related | [Create Group](/sdk/flutter/create-group), [Join Group](/sdk/flutter/join-group), [Retrieve Groups](/sdk/flutter/retrieve-groups) | + + + +Groups let users converse together in a shared space. CometChat supports three group types (public, private, password-protected) and four member roles with different permission levels. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Group Types + +| Type | Description | Join Behavior | +|------|-------------|---------------| +| **Public** | Open to all users | Any user can join without approval | +| **Private** | Invite-only | Users must be added by admin/moderator | +| **Password** | Protected by password | Users must provide correct password to join | + +## Member Roles + +| Role | Permissions | +|------|-------------| +| **Owner** | Full control: manage members, settings, delete group. Cannot leave without transferring ownership. | +| **Admin** | Manage members (add, kick, ban), change member scope, update group settings | +| **Moderator** | Kick and ban members, moderate content | +| **Member** | Send/receive messages, leave group | + +## Available Operations + +- [Create a Group](/sdk/flutter/create-group) — Create public, private, or password-protected groups +- [Join a Group](/sdk/flutter/join-group) — Join existing groups as a participant +- [Leave a Group](/sdk/flutter/leave-group) — Leave a group you're a member of +- [Update a Group](/sdk/flutter/update-group) — Update group name, description, icon, and settings +- [Delete a Group](/sdk/flutter/delete-group) — Permanently delete a group (owner only) +- [Transfer Ownership](/sdk/flutter/transfer-group-ownership) — Transfer group ownership to another member +- [Retrieve Groups](/sdk/flutter/retrieve-groups) — Fetch and filter the list of groups +- [Retrieve Group Members](/sdk/flutter/retrieve-group-members) — Get the member list for a group +- [Add Members](/sdk/flutter/group-add-members) — Add users to a group +- [Kick & Ban Members](/sdk/flutter/group-kick-ban-members) — Remove or ban members from a group +- [Change Member Scope](/sdk/flutter/group-change-member-scope) — Promote or demote members + +--- + +## Next Steps + + + + Create public, private, or password-protected groups + + + Join existing groups as a participant + + + Fetch and filter the list of groups + + + Get the member list for a group + + diff --git a/sdk/flutter/v4/interactive-messages.mdx b/sdk/flutter/v4/interactive-messages.mdx new file mode 100644 index 000000000..e13d96a3e --- /dev/null +++ b/sdk/flutter/v4/interactive-messages.mdx @@ -0,0 +1,579 @@ +--- +title: "Interactive Messages" +sidebarTitle: "Interactive Messages" +description: "Send and receive interactive messages with embedded forms, buttons, and other UI elements using the CometChat Flutter SDK." +--- + + + +| Field | Value | +| --- | --- | +| Key Classes | `InteractiveMessage`, `InteractionGoal`, `Interaction`, `InteractionReceipt` | +| Key Methods | `CometChat.sendInteractiveMessage()`, `CometChat.markAsInteracted()` | +| Key Constants | `InteractionGoalType.anyAction`, `InteractionGoalType.anyOf`, `InteractionGoalType.allOf`, `InteractionGoalType.none` | +| Listener Events | `onInteractiveMessageReceived`, `onInteractionGoalCompleted` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Create and send an interactive form message +InteractiveMessage interactiveMessage = InteractiveMessage( + interactiveData: interactiveData, // Map with form fields + receiverUid: "UID", + type: "form", + receiverType: CometChatReceiverType.user, + allowSenderInteraction: true, + interactionGoal: InteractionGoal(type: InteractionGoalType.none), +); +CometChat.sendInteractiveMessage(interactiveMessage, + onSuccess: (InteractiveMessage message) {}, + onError: (CometChatException e) {}, +); + +// Mark as interacted +CometChat.markAsInteracted(messageId, elementId, + onSuccess: (String message) {}, + onError: (CometChatException e) {}, +); + +// Listen for interactive messages +CometChat.addMessageListener("LISTENER_ID", this); +// onInteractiveMessageReceived(InteractiveMessage message) { } +// onInteractionGoalCompleted(InteractionReceipt receipt) { } +``` + + + +Interactive messages embed UI elements like forms, buttons, and dropdowns directly within chat messages. Users can interact with these elements without leaving the conversation, enabling surveys, quick actions, and data collection. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## InteractiveMessage + +The `InteractiveMessage` class extends `BaseMessage` and represents a message with embedded interactive content. + +| Parameter | Type | Description | Required | +| --- | --- | --- | --- | +| `receiverUid` | `String` | The UID of the user or GUID of the group | Yes | +| `receiverType` | `String` | `CometChatReceiverType.user` or `CometChatReceiverType.group` | Yes | +| `type` | `String` | Type identifier (e.g., `"form"`, `"card"`) | Yes | +| `interactiveData` | `Map` | JSON structure defining the interactive elements | Yes | +| `allowSenderInteraction` | `bool` | Whether sender can interact with the message | No (default: `false`) | +| `interactionGoal` | `InteractionGoal` | Defines when the interaction is considered complete | No (default: `none`) | +| `tags` | `List` | Tags associated with the message | No | +| `muid` | `String` | Unique message identifier for deduplication | No | +| `parentMessageId` | `int` | ID of the parent message (for threads) | No | +| `metadata` | `Map` | Custom metadata attached to the message | No | + +## Send an Interactive Message + +Use `CometChat.sendInteractiveMessage()` to send an interactive message. + + + +```dart +final Map interactiveData = { + "title": "Survey", + "formFields": [ + { + "elementType": "textInput" , + "elementId": "name", + "optional": false, + "label": "Name", + "placeholder": { + "text": "Enter text here" + } + }, + { + "elementType": "textInput", + "elementId": "age", + "optional": true, + "label": "Age", + "maxLines": 1, + "placeholder": { + "text": "Enter text here" + } + }, + { + "elementType": "Select", + "elementId": "checkBox1", + "optional": true, + "label": "Check box element", + "defaultValue":["chk_option_2"], + "options" : [ + { + "label": "Option 1", + "value": "chk_option_1", + }, + { + "label": "Option 2", + "value": "chk_option_2", + } + + ] + }, + { + "elementType": "dropdown", + "elementId": "gender", + "optional": false, + "label": "Gender", + "defaultValue":"male", + "options" : [ + { + "label": "Male", + "value": "male", + }, + { + "label": "Female", + "value": "female", + } + + ] + }, + ], + "submitElement": { + "elementType": "button", + "elementId": "submitButton", + "buttonText": "Submit", + "disableAfterInteracted": false, + "action": { + "actionType": "urlNavigation", + "url": "https://www.cometchat.com/", + } + }, +}; + + + +InteractiveMessage interactiveMessage = +InteractiveMessage(interactiveData: interactiveData, + receiverUid: "cometchat-uid-3", //Replace this with receiver id + type: "form", + receiverType: "user",//replace this with receiver type + allowSenderInteraction: true, + muid: DateTime.now().millisecondsSinceEpoch.toString(), + interactionGoal: InteractionGoal( + type: InteractionGoalType.none + ) + ); + + +CometChat.sendInteractiveMessage(interactiveMessage, onSuccess: (InteractiveMessage message){ + +print(interactiveMessage); + +}, onError: (CometChatException excep){ +print(excep); +}); +``` + + + + +The `sendInteractiveMessage()` method returns an `InteractiveMessage` object on success. + + +**On Success** — An `InteractiveMessage` object containing all details of the sent interactive message: + + + +**InteractiveMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `501` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#send-interactive-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-3"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-3"` | +| `type` | string | Type of the message | `"form"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#send-interactive-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"interactive"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `interactiveData` | object | Structured data for the interactive element | `{"title": "Survey", "formFields": [...]}` | +| `interactions` | array | List of user interactions with the message | `[]` | +| `interactionGoal` | object | Intended outcome of interacting with the message | `{"type": "none", "elementIds": []}` | +| `tags` | array | List of tags associated with the message | `[]` | +| `allowSenderInteraction` | boolean | Whether the sender can interact with the message | `true` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-3"` | +| `name` | string | Display name of the receiver | `"Kevin Hart"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to send the interactive message."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Interactive Elements + +The `interactiveData` map defines the UI elements. Common element types: + +| Element Type | Description | +| --- | --- | +| `textInput` | Single or multi-line text field | +| `dropdown` | Single-select dropdown menu | +| `Select` | Multi-select checkbox group | +| `button` | Clickable button with action | + +### Text Input + +```dart +{ + "elementType": "textInput", + "elementId": "name", + "optional": false, + "label": "Name", + "maxLines": 1, // Optional: limit to single line + "placeholder": {"text": "Enter text here"} +} +``` + +### Dropdown (Single Select) + +```dart +{ + "elementType": "dropdown", + "elementId": "country", + "optional": false, + "label": "Country", + "defaultValue": "us", + "options": [ + {"label": "United States", "value": "us"}, + {"label": "United Kingdom", "value": "uk"}, + {"label": "Canada", "value": "ca"} + ] +} +``` + +### Checkbox (Multi Select) + +```dart +{ + "elementType": "Select", + "elementId": "preferences", + "optional": true, + "label": "Preferences", + "defaultValue": ["email"], + "options": [ + {"label": "Email notifications", "value": "email"}, + {"label": "SMS notifications", "value": "sms"}, + {"label": "Push notifications", "value": "push"} + ] +} +``` + +### Submit Button + +```dart +{ + "elementType": "button", + "elementId": "submitBtn", + "buttonText": "Submit", + "disableAfterInteracted": true, + "action": { + "actionType": "urlNavigation", + "url": "https://example.com/submit" + } +} +``` + +## Interaction Goals + +An `InteractionGoal` defines when the user's interaction with the message is considered complete. Use this to track engagement and trigger follow-up actions. + +`InteractionGoal` Parameters: + +| Parameter | Type | Description | Required | +| --- | --- | --- | --- | +| `type` | `String` | The type of interaction goal (see goal types below) | Yes | +| `elementIds` | `List` | List of element IDs associated with this goal | No (default: `[]`) | + +| Goal Type | Constant | Description | +| --- | --- | --- | +| Any Interaction | `InteractionGoalType.anyAction` | Complete when any element is interacted with | +| Any of Specific | `InteractionGoalType.anyOf` | Complete when any of the specified elements is interacted with | +| All of Specific | `InteractionGoalType.allOf` | Complete when all specified elements are interacted with | +| None | `InteractionGoalType.none` | Never considered complete (default) | + +### Set an Interaction Goal + + + +```dart +List elementIds = ["name", "gender", "submitButton"]; +InteractionGoal interactionGoal = InteractionGoal( + type: InteractionGoalType.allOf, + elementIds: elementIds, +); + +InteractiveMessage interactiveMessage = InteractiveMessage( + interactiveData: interactiveData, + receiverUid: "cometchat-uid-3", + type: "form", + receiverType: "user", + interactionGoal: interactionGoal, +); + +CometChat.sendInteractiveMessage(interactiveMessage, + onSuccess: (InteractiveMessage message) { + debugPrint("Interactive message sent successfully: $message"); + }, + onError: (CometChatException excep) { + debugPrint("Interactive message sending failed with error: ${excep.message}"); + }, +); +``` + + + + +You would be tracking every interaction users perform on an `InteractiveMessage` (captured as `Interaction` objects) and comparing those with the defined `InteractionGoal`. The completion of a goal can vary depending on the goal type. + +This user interaction tracking mechanism provides a flexible and efficient way to monitor user engagement within an interactive chat session. By defining clear interaction goals and checking user interactions against these goals, you can manage user engagement and improve the overall chat experience in your CometChat-enabled application. + +## Interactions + +An `Interaction` represents a single user action on an interactive element. + +| Property | Type | Description | +| --- | --- | --- | +| `elementId` | `String` | Identifier of the interacted element | +| `interactedAt` | `DateTime` | Timestamp indicating when the interaction occurred | + +## Mark as Interacted + +Use `CometChat.markAsInteracted()` to record when a user interacts with an element. This method marks a message as interacted by identifying it with the provided ID and logs the interactive element associated with the interaction. + + + +```dart +Future recordInteraction() async { + int messageId = 0; // set to your interactive message id + String interactedElementId = ""; // set to the element id + + await CometChat.markAsInteracted( + messageId, + interactedElementId, + onSuccess: (String message) { + // after success + }, + onError: (CometChatException excep) { + // after error + }, + ); +} +``` + + + + +| Parameter | Type | Description | Required | +| --- | --- | --- | --- | +| `messageId` | `int` | The ID of the interactive message | Yes | +| `interactedElementId` | `String` | The element ID that was interacted with | Yes | + + +**On Success** — A `String` message confirming the interaction was recorded: + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"Message marked as interacted successfully."` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to mark the message as interacted."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Real-Time Events + +Register a `MessageListener` to receive interactive message events. + +### Receive Interactive Messages + +The `onInteractiveMessageReceived` event listener is triggered when an `InteractiveMessage` is received. + + + +```dart +class DemoClass with MessageListener{ + +String listenerId = "UNIQUE_LISTENER_ID"; //Replace with unique listener id + + +CometChat.addMessageListener(_listenerId, this); + + + + @override +void onInteractiveMessageReceived(InteractiveMessage message){ + + //TODO implement onInteractiveMessageReceived event +} + +} +``` + + + + +### Interaction Goal Completed + +The `onInteractionGoalCompleted` event listener is invoked when an interaction goal is achieved. + +`InteractionReceipt` Properties: + +| Property | Type | Description | +| --- | --- | --- | +| `messageId` | `int` | The ID of the interactive message | +| `sender` | `User` | The user who completed the interaction | +| `receiverType` | `String` | Type of the receiver (`"user"` or `"group"`) | +| `receiverId` | `String` | UID or GUID of the receiver | +| `interactions` | `List` | List of interactions that satisfied the goal | + + + +```dart +class DemoClass with MessageListener{ + +String listenerId = "UNIQUE_LISTENER_ID"; //Replace with unique listener id + + +CometChat.addMessageListener(_listenerId, this); + + + +@override +void onInteractionGoalCompleted(InteractionReceipt receipt) { + //TODO implement onInteractionGoalCompleted +} + +} +``` + + + + +These event listeners offer your application a way to provide real-time updates in response to incoming interactive messages and goal completions, contributing to a more dynamic and responsive user chat experience. + + +Always remove listeners when they're no longer needed to prevent memory leaks. + +```dart +CometChat.removeMessageListener("UNIQUE_LISTENER_ID"); +``` + + +## Allow Sender Interaction + +By default, the sender cannot interact with their own interactive message. Set `allowSenderInteraction` to `true` to enable sender interaction: + + + +```dart +InteractiveMessage interactiveMessage = InteractiveMessage( + interactiveData: interactiveData, + receiverUid: "cometchat-uid-3", + type: "form", + receiverType: "user", + allowSenderInteraction: true, +); + +CometChat.sendInteractiveMessage(interactiveMessage, + onSuccess: (InteractiveMessage message) { + debugPrint("Interactive message sent successfully: $message"); + }, + onError: (CometChatException excep) { + debugPrint("Interactive message sending failed with error: ${excep.message}"); + }, +); +``` + + + + +--- + +## Next Steps + + + + Send text, media, and custom messages + + + Listen for incoming messages in real time + + + Send ephemeral messages that aren't stored + + + Understand message types and hierarchy + + diff --git a/sdk/flutter/v4/join-group.mdx b/sdk/flutter/v4/join-group.mdx new file mode 100644 index 000000000..bc2595378 --- /dev/null +++ b/sdk/flutter/v4/join-group.mdx @@ -0,0 +1,166 @@ +--- +title: "Join A Group" +sidebarTitle: "Join Group" +description: "Learn how to join public and password-protected groups using the CometChat Flutter SDK." +--- + + + +```dart +// Join a public group +CometChat.joinGroup("GROUP_GUID", CometChatGroupType.public, + onSuccess: (Group group) => debugPrint("Joined: ${group.name}"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Join a password-protected group +CometChat.joinGroup("GROUP_GUID", CometChatGroupType.password, + password: "group_password", + onSuccess: (Group group) => debugPrint("Joined: ${group.name}"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); +``` + +**Group types:** `CometChatGroupType.public` | `CometChatGroupType.password` | `CometChatGroupType.private` + + +Join a group to start sending and receiving messages in it. Public groups can be joined freely, password groups require the correct password, and private groups require an admin to add you (no direct join). + +## Join a Group + +Use `joinGroup()` to join a group. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `guid` | `String` | The GUID of the group to join | +| `groupType` | `String` | `CometChatGroupType.public`, `CometChatGroupType.password`, or `CometChatGroupType.private` | +| `password` | `String` | Required for password-protected groups (defaults to empty string) | +| `onSuccess` | `Function(Group group)?` | Callback triggered on successful join | +| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | + + + +```dart +String GUID = "GUID"; +String groupType = CometChatGroupType.public; +String password = ""; + +CometChat.joinGroup(GUID, groupType, password: password, + onSuccess: (Group group) { + debugPrint("Group Joined Successfully : $group "); + }, onError: (CometChatException e) { + debugPrint("Group Joining failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `Group` object containing all details of the joined group: + + + +**Group Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | +| `name` | string | Display name of the group | `"Hello Group!"` | +| `icon` | string | URL of the group icon | `null` | +| `description` | string | Description of the group | `null` | +| `membersCount` | number | Number of members in the group | `5` | +| `metadata` | object | Custom metadata attached to the group | `{}` | +| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | +| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | +| `createdAt` | number | Epoch timestamp when the group was created | `1745551200` | +| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | +| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | +| `tags` | array | List of tags associated with the group | `[]` | +| `type` | string | Type of the group (public, private, password) | `"public"` | +| `scope` | string | Scope of the logged-in user in the group | `"participant"` | +| `password` | string | Password for password-protected groups | `null` | +| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified group does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | + + + +Once joined, you can send and receive messages in the group. CometChat tracks joined groups — you don't need to rejoin each session. Check `hasJoined` on the [`Group`](/sdk/reference/entities#group) object to verify membership. + +## Real-time Group Member Joined Events + +*In other words, as a member of a group, how do I know if someone joins the group when my app is running?* + +If a user joins any group, the members of the group receive a real-time event in the `onGroupMemberJoined()` method of the `GroupListener` class. + + + +```dart +class ClassName with GroupListener { + // CometChat.addGroupListener("group_Listener_id", this); + + @override + void onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup) { + debugPrint("onGroupMemberJoined"); + } +} +``` + + + + + + +Always remove group listeners when they're no longer needed (e.g., in `dispose()`). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +CometChat.removeGroupListener("group_Listener_id"); +``` + + +## Missed Group Member Joined Events + +*In other words, as a member of a group, how do I know if someone joins the group when my app is not running?* + +When you retrieve the list of previous messages if a member has joined any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. + +For the group member joined event, in the `Action` object received, the following fields can help you get the relevant information- + +1. `action` - `joined` +2. `actionBy` - User object containing the details of the user who joined the group +3. `actionFor`- Group object containing the details of the group the user has joined + +--- + +## Next Steps + + + + Allow members to leave a group + + + Fetch the list of members in a group + + + Send messages to group conversations + + + Programmatically add members to a group + + diff --git a/sdk/flutter/key-concepts.mdx b/sdk/flutter/v4/key-concepts.mdx similarity index 100% rename from sdk/flutter/key-concepts.mdx rename to sdk/flutter/v4/key-concepts.mdx diff --git a/sdk/flutter/v4/leave-group.mdx b/sdk/flutter/v4/leave-group.mdx new file mode 100644 index 000000000..3d57928d7 --- /dev/null +++ b/sdk/flutter/v4/leave-group.mdx @@ -0,0 +1,149 @@ +--- +title: "Leave A Group" +sidebarTitle: "Leave Group" +description: "Learn how to leave a group and receive real-time events when members leave using the CometChat Flutter SDK." +--- + + + +```dart +// Leave a group +CometChat.leaveGroup("GROUP_GUID", + onSuccess: (String message) => debugPrint("Left group: $message"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Listen for group member left events +CometChat.addGroupListener("listener_id", GroupListener( + onGroupMemberLeft: (Action action, User leftUser, Group leftGroup) { + debugPrint("${leftUser.name} left ${leftGroup.name}"); + }, +)); +``` + + +Leave a group to stop receiving messages and updates from it. Once you leave, you'll need to rejoin to participate again. + + +Group owners cannot leave without first transferring ownership to another member. See [Transfer Group Ownership](/sdk/flutter/transfer-group-ownership). + + +## Leave a Group + +Use `leaveGroup()` to leave a group. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `guid` | `String` | The GUID of the group you would like to leave | +| `onSuccess` | `Function(String returnResponse)?` | Callback triggered on successful leave | +| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | + + + +```dart +String GUID = "GUID"; + +CometChat.leaveGroup(GUID, onSuccess: ( String message) { + debugPrint("Group Left Successfully : $message"); + },onError: (CometChatException e) { + debugPrint("Group Left failed : ${e.message}"); + }); +``` + + + + + + +**On Success** — A `String` message confirming the operation: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"cometchat-guid-1 left successfully"` | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_NOT_A_MEMBER"` | +| `message` | string | Human-readable error message | `"The user is not a member of this group."` | +| `details` | string | Additional technical details | `"Cannot leave a group that you are not a member of."` | + + + +Once a group is left, the user will no longer receive any updates or messages pertaining to the group. + +## Real-time Group Member Left Events + +*In other words, as a member of a group, how do I know if someone has left it when my app is running?* + +If a user leaves a group, the members of the group receive a real-time event in the `onGroupMemberLeft()` method of the `GroupListener` class. + + + +```dart +class Class_Name with GroupListener { + +//CometChat.addGroupListener("group_Listener_id", this); +@override +void onGroupMemberLeft(Action action, User leftUser, Group leftGroup) { + print("onGroupMemberLeft "); + +} +} +``` + + + + + + +Always remove group listeners when they're no longer needed (e.g., in `dispose()`). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +CometChat.removeGroupListener("group_Listener_id"); +``` + + +## Missed Group Member Left Events + +*In other words, as a member of a group, how do I know if someone has left it when my app is not running?* + +When you retrieve the list of previous messages if a member has left any group that the logged-in user is a member of, the list of messages will contain an [`Action`](/sdk/reference/messages#action) message. An `Action` message is a sub-class of [`BaseMessage`](/sdk/reference/messages#basemessage) class. + +For the group member left event, in the `Action` object received, the following fields can help you get the relevant information- + +1. `action` - `left` +2. `actionBy` - User object containing the details of the user who left the group +3. `actionFor` - Group object containing the details of the group the user has left + +--- + +## Next Steps + + + + Join public or password-protected groups + + + Permanently delete a group (admin only) + + + Remove or ban members from a group + + + Fetch and filter the list of groups + + diff --git a/sdk/flutter/v4/mentions.mdx b/sdk/flutter/v4/mentions.mdx new file mode 100644 index 000000000..d837732e7 --- /dev/null +++ b/sdk/flutter/v4/mentions.mdx @@ -0,0 +1,596 @@ +--- +title: "Mentions" +sidebarTitle: "Mentions" +description: "Send messages with user mentions, retrieve mentioned users, and filter messages by mention metadata using the CometChat Flutter SDK." +--- + + + +| Field | Value | +| --- | --- | +| Key Classes | `TextMessage`, `BaseMessage`, `User`, `MessagesRequest`, `MessagesRequestBuilder` | +| Key Properties | `mentionedUsers`, `hasMentionedMe` | +| Builder Methods | `mentionsWithTagInfo()`, `mentionsWithBlockedInfo()` | +| Mention Format | `<@uid:UID>` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Send a message with a mention (use <@uid:UID> format) +TextMessage textMessage = TextMessage( + text: "Hello <@uid:cometchat-uid-1>", + receiverUid: "UID", + receiverType: CometChatReceiverType.user, + type: CometChatMessageType.text, +); +CometChat.sendMessage(textMessage, onSuccess: (msg) { + debugPrint("Mentioned users: ${msg.mentionedUsers}"); +}, onError: (e) {}); + +// Get mentioned users from a message +List mentionedUsers = message.mentionedUsers; + +// Check if logged-in user was mentioned +bool wasMentioned = message.hasMentionedMe ?? false; + +// Fetch messages with mention tag info +MessagesRequest request = (MessagesRequestBuilder() + ..uid = "UID" + ..limit = 30 + ..mentionsWithTagInfo(true) +).build(); +request.fetchPrevious(onSuccess: (messages) {}, onError: (e) {}); +``` + + + +Mentions in messages enable users to refer to specific individuals within a conversation. This is done by using the `<@uid:UID>` format, where `UID` represents the user's unique identification. + +Mentions are a powerful tool for enhancing communication in messaging platforms. They streamline interaction by allowing users to easily engage and collaborate with particular individuals, especially in group conversations. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Send Mentioned Messages + +Every User object has a String unique identifier associated with them which can be found in a property called uid. To mention a user in a message, the message text should contain the uid in following format: `<@uid:UID_OF_THE_USER>`. For example, to mention the user with UID cometchat-uid-1 in a text message, your text should be "`<@uid:cometchat-uid-1>`" + + + +```dart +String receiverID = "UID"; +User sender = User(name: "Sender", uid: "senderUID"); +String messageText = "Hello, <@uid:cometchat-uid-1>"; +String receiverType = CometChatReceiverType.user; + +TextMessage textMessage = TextMessage( + text: messageText, + sender: sender, + receiverUid: receiverID, + receiverType: receiverType, + category: CometChatMessageCategory.message, + type: CometChatMessageType.text); + +CometChat.sendMessage(textMessage, onSuccess:(TextMessage textMessage) { + print("Message sent successfully: ${textMessage.text}, mentioned users: ${textMessage.mentionedUsers}"); + }, + onError:(CometChatException e) { + print("Message sending failed with exception: $e"); + } +); +``` + + + + +```dart +String receiverID = "GUID"; +User sender = User(name: "Sender", uid: "senderUID"); +String messageText = "Hello, <@uid:cometchat-uid-1>"; +String receiverType = CometChatReceiverType.group; + +TextMessage textMessage = TextMessage( + text: messageText, + sender: sender, + receiverUid: receiverID, + receiverType: receiverType, + category: CometChatMessageCategory.message, + type: CometChatMessageType.text); + +CometChat.sendMessage(textMessage, onSuccess:(TextMessage textMessage) { + print("Message sent successfully: ${textMessage.text}, mentioned users: ${textMessage.mentionedUsers}"); + }, + onError:(CometChatException e) { + print("Message sending failed with exception: $e"); + } +); +``` + + + + + + +**On Success** — A `TextMessage` object containing all details of the sent message with mentions: + + + +**TextMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `501` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-mentions-send-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-mentions-send-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `text` | string | The text content of the message | `"Hello, <@uid:cometchat-uid-1>"` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | [See below ↓](#fetch-mentions-send-mentionedusers-object) | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `moderationStatus` | string | Moderation status of the message | `null` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + +--- + + + +**`mentionedUsers` Array (per item):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the mentioned user | `"cometchat-uid-1"` | +| `name` | string | Display name of the mentioned user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to send the message."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + + + +You can mention user in text message and media messages captions + + + +## Mentioned Messages + +By default, the SDK will fetch all the messages irrespective of the fact that the logged-in user is mentioned or not in the message. The SDK has other optional filters such as tag info and blocked info. + +| Setting | Description | Default Value | +| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| `mentionsWithTagInfo(bool value)` | If set to `true`, SDK will fetch a list of messages where users are mentioned & will also fetch the tags of the mentioned users. | false | +| `mentionsWithBlockedInfo(bool value)` | If set to `true`, SDK will fetch a list of messages where users are mentioned & will also fetch their blocked relationship with the logged-in user. | false | + +## Mentions With Tag Info + +To get a list of messages in a conversation where users are mentioned along with the tags of the mentioned users. + +Relevant fields to access on returned messages and their mentioned users: + +| Field | Property | Return Type | Description | +|-------|----------|-------------|-------------| +| mentionedUsers | `mentionedUsers` | [`List`](/sdk/reference/entities#user) | Array of users mentioned in the message | +| tags (on each mentioned user) | `tags` | `List` | Tags associated with the mentioned user | + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messagesRequest = (MessagesRequestBuilder() +..limit=50 +..uid=UID +..mentionsWithTagInfo(true)) +.build(); + +messagesRequest.fetchPrevious(onSuccess:(List messages) { + for (BaseMessage message in messages){ + for (User user in message.mentionedUsers){ + print("mentioned user tags: ${user.tags}"); + } + } + }, + onError: (CometChatException e) { + print("error: $e"); + } +); +``` + + + + +```dart +String GUID = "cometchat-guid-1"; + +MessagesRequest messagesRequest = (MessagesRequestBuilder() +..limit=50 +..guid=GUID +..mentionsWithTagInfo(true)) +.build(); + +messagesRequest.fetchPrevious(onSuccess:(List messages) { + for (BaseMessage message in messages){ + for (User user in message.mentionedUsers){ + print("mentioned user tags: ${user.tags}"); + } + } + }, + onError: (CometChatException e) { + print("error: $e"); + } +); +``` + + + + + + +**On Success** — A `List` containing messages with mentioned users and their tag info (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `502` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-mentions-taginfo-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-mentions-taginfo-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Mentions With Blocked Info + +To get a list of messages in a conversation where users are mentioned along with the blocked relationship of the mentioned users with the logged-in user. + +Relevant fields to access on returned messages and their mentioned users: + +| Field | Property | Return Type | Description | +|-------|----------|-------------|-------------| +| mentionedUsers | `mentionedUsers` | [`List`](/sdk/reference/entities#user) | Array of users mentioned in the message | +| blockedByMe (on each mentioned user) | `blockedByMe` | `bool` | Whether the logged-in user has blocked this mentioned user | +| hasBlockedMe (on each mentioned user) | `hasBlockedMe` | `bool` | Whether this mentioned user has blocked the logged-in user | + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messagesRequest = (MessagesRequestBuilder() +..limit=50 +..uid=UID +..mentionsWithBlockedInfo(true)) +.build(); + +messagesRequest.fetchPrevious(onSuccess:(List messages) { + for (BaseMessage message in messages){ + for (User user in message.mentionedUsers){ + print("mentioned user has blocked me? ${user.hasBlockedMe}"); + print("mentioned user is blocked by me? ${user.blockedByMe}"); + } + } + }, + onError: (CometChatException e) { + print("error: $e"); + } +); +``` + + + + +```dart +String GUID = "cometchat-guid-1"; + +MessagesRequest messagesRequest = (MessagesRequestBuilder() +..limit=50 +..guid=GUID +..mentionsWithBlockedInfo(true)) +.build(); + +messagesRequest.fetchPrevious(onSuccess:(List messages) { + for (BaseMessage message in messages){ + for (User user in message.mentionedUsers){ + print("mentioned user has blocked me? ${user.hasBlockedMe}"); + print("mentioned user is blocked by me? ${user.blockedByMe}"); + } + } + }, + onError: (CometChatException e) { + print("error: $e"); + } +); +``` + + + + + + +**On Success** — A `List` containing messages with mentioned users and their blocked relationship info (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `503` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-mentions-blockedinfo-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-mentions-blockedinfo-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Get Users Mentioned In a Particular Message + +To retrieve the list of users mentioned in the particular message, you can use the `mentionedUsers` property on any `BaseMessage`. This property will return an array containing User objects of the mentioned users, or an empty array if no users were mentioned in the message. + + + +```dart +message.mentionedUsers +``` + + + + + +The `mentionedUsers` property returns a `List<`[`User`](/sdk/reference/entities#user)`>` objects. + +## Check if Logged-in user has been mentioned + +To check if the logged-in user has been mentioned in a particular message we can use the `hasMentionedMe` property on any `BaseMessage`. This property will return a boolean value, true if the logged-in user has been mentioned, otherwise `false`. + + + +```dart +message.hasMentionedMe +``` + + + + + +--- + +## Next Steps + + + + Send text, media, and custom messages + + + Listen for incoming messages in real time + + + Add emoji reactions to messages + + + Organize conversations with message threads + + diff --git a/sdk/flutter/message-structure-and-hierarchy.mdx b/sdk/flutter/v4/message-structure-and-hierarchy.mdx similarity index 100% rename from sdk/flutter/message-structure-and-hierarchy.mdx rename to sdk/flutter/v4/message-structure-and-hierarchy.mdx diff --git a/sdk/flutter/v4/overview.mdx b/sdk/flutter/v4/overview.mdx new file mode 100644 index 000000000..6789301ed --- /dev/null +++ b/sdk/flutter/v4/overview.mdx @@ -0,0 +1,149 @@ +--- +title: "Flutter SDK" +sidebarTitle: "Overview" +description: "Add real-time chat, voice, and video to your Flutter application with the CometChat SDK." +--- + +{/* TL;DR for Agents and Quick Reference */} + + +```yaml +# Install (pubspec.yaml) +cometchat_sdk: ^4.0.33 +``` + +```dart +import 'package:cometchat_sdk/cometchat_sdk.dart'; + +// 1. Initialize (once at app startup) +AppSettings appSettings = (AppSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.allUsers + ..region = "APP_REGION" // e.g. "us", "eu", "in" + ..autoEstablishSocketConnection = true +).build(); + +CometChat.init("APP_ID", appSettings, + onSuccess: (msg) => debugPrint("Init success"), + onError: (e) => debugPrint("Init failed: ${e.message}"), +); + +// 2. Login (check session first) +CometChat.getLoggedInUser( + onSuccess: (user) { + if (user == null) { + CometChat.login("cometchat-uid-1", "AUTH_KEY", // dev only — use Auth Token in production + onSuccess: (user) => debugPrint("Login success"), + onError: (e) => debugPrint("Login failed"), + ); + } + }, + onError: (e) => debugPrint("Error: ${e.message}"), +); +``` + +**Credentials:** App ID, Region, Auth Key from [CometChat Dashboard](https://app.cometchat.com) +**Test UIDs:** `cometchat-uid-1` through `cometchat-uid-5` + + +The CometChat Flutter SDK lets you add real-time messaging, voice, and video calling to any Flutter application — iOS, Android, Web, or Desktop. + +## Requirements + +| Requirement | Minimum Version | +|-------------|-----------------| +| Flutter SDK | 1.2 | +| Android API Level | 21 | +| iOS | 11 | + +Requires AndroidX compatibility. For iOS platform setup, see [Setup](/sdk/flutter/setup#ios-setup). + +## Getting Started + + + + [Sign up for CometChat](https://app.cometchat.com) and create an app. Note your App ID, Region, and Auth Key from the Dashboard. + + + Add the SDK to your project and initialize it with your credentials. See [Setup](/sdk/flutter/setup). + + + Log in users with Auth Key (development) or Auth Token (production). See [Authentication](/sdk/flutter/authentication-overview). + + + Send your first message, make a call, or manage users and groups. + + + +## Features + + + + 1:1 and group chat, threads, reactions, typing indicators, read receipts, file sharing, and interactive messages. + + + Ringing flows, direct call sessions, standalone calling, recording, and screen sharing. + + + Create, retrieve, and manage users. Track online/offline presence and block/unblock users. + + + Public, private, and password-protected groups with member management, roles, and ownership transfer. + + + +## Sample Apps + +Explore working examples with full source code: + + + + Flutter sample app + + + +## UI Kits + +Skip the UI work — use pre-built, customizable components: + + + + Flutter UI Kit + + + +## Resources + + + + UIDs, GUIDs, auth tokens, and core SDK concepts + + + Message categories, types, and hierarchy + + + Latest SDK version and release notes + + + Migration guide for V3 users + + + Common issues and solutions + + + +## Next Steps + + + + Install and initialize the CometChat Flutter SDK + + + Log in users with Auth Key or Auth Token + + + Send your first text, media, or custom message + + + UIDs, GUIDs, auth tokens, and core SDK concepts + + diff --git a/sdk/flutter/rate-limits.mdx b/sdk/flutter/v4/rate-limits.mdx similarity index 100% rename from sdk/flutter/rate-limits.mdx rename to sdk/flutter/v4/rate-limits.mdx diff --git a/sdk/flutter/v4/reactions.mdx b/sdk/flutter/v4/reactions.mdx new file mode 100644 index 000000000..7f0c3e057 --- /dev/null +++ b/sdk/flutter/v4/reactions.mdx @@ -0,0 +1,576 @@ +--- +title: "Reactions" +sidebarTitle: "Reactions" +description: "Add, remove, and fetch emoji reactions on messages in your Flutter chat application using CometChat SDK." +--- + + + +| Field | Value | +| --- | --- | +| Key Classes | `BaseMessage`, `Reaction`, `ReactionCount`, `ReactionEvent`, `ReactionsRequest`, `ReactionsRequestBuilder` | +| Key Methods | `CometChat.addReaction()`, `CometChat.removeReaction()`, `ReactionsRequest.fetchNext()`, `ReactionsRequest.fetchPrevious()`, `CometChatHelper.updateMessageWithReactionInfo()` | +| Listener Events | `onMessageReactionAdded`, `onMessageReactionRemoved` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Add a reaction to a message +CometChat.addReaction(messageId, "\U0001f600", onSuccess: (message) { + debugPrint("Reaction added: ${message.getReactions().last}"); +}, onError: (e) {}); + +// Remove a reaction from a message +CometChat.removeReaction(messageId, "\U0001f600", onSuccess: (message) { + debugPrint("Reaction removed"); +}, onError: (e) {}); + +// Fetch reactions for a message +ReactionsRequest request = (ReactionsRequestBuilder() + ..messageId = messageId + ..limit = 30 +).build(); +request.fetchNext(onSuccess: (reactions) {}, onError: (e) {}); + +// Listen for reaction events +CometChat.addMessageListener("LISTENER_ID", MessageListener( + onMessageReactionAdded: (ReactionEvent event) {}, + onMessageReactionRemoved: (ReactionEvent event) {}, +)); +``` + + + +Reactions let users respond to messages with emoji. You can add or remove reactions, fetch all reactions on a message, listen for reaction events in real time, and update your UI when reactions change. + +Reactions work on text messages, media messages, and custom messages. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Add a Reaction + +Call `addReaction()` with a message ID and an emoji string. + +| Parameter | Type | Description | +| --- | --- | --- | +| `messageId` | `int` | The ID of the message to react to. | +| `reaction` | `String` | The reaction emoji (e.g., `"\U0001f600"`, `"\U0001f44d"`). | + + + +```dart +int messageId = 1; + +CometChat.addReaction(messageId, "\U0001f634", onSuccess: (message) { + debugPrint("Success : ${message.getReactions().last}"); +}, onError: (e) { + debugPrint("Error: ${e.message}"); +}); +``` + + + + +**On Success** — A `BaseMessage` object representing the message with the reaction added: + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `401` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below \u2193](#add-reaction-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554750` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below \u2193](#add-reaction-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to process the reaction."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Remove a Reaction + +Call `removeReaction()` with the same message ID and emoji. + +| Parameter | Type | Description | +| --- | --- | --- | +| `messageId` | `int` | The ID of the message to remove the reaction from. | +| `reaction` | `String` | The reaction emoji to remove (e.g., `"\U0001f600"`, `"\U0001f44d"`). | + + + +```dart +int messageId = 1; + +CometChat.removeReaction(messageId, "\U0001f634", onSuccess: (message) { + debugPrint("Success : ${message.getReactions().last}"); +}, onError: (e) { + debugPrint("Error: ${e.message}"); +}); +``` + + + + +**On Success** — A `BaseMessage` object representing the message with the reaction removed: + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `401` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below \u2193](#remove-reaction-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554750` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below \u2193](#remove-reaction-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to process the reaction."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +Both `addReaction()` and `removeReaction()` return a [`BaseMessage`](/sdk/reference/messages#basemessage) object with the updated reactions. + +## Read Reaction Data from a Message + +Any `BaseMessage` exposes reaction data through its `reactions` property: + +### Get All Reactions + +Use the `reactions` property to retrieve the list of reactions on a message. Returns a `List<`[`ReactionCount`](/sdk/reference/auxiliary#reactioncount)`>` containing the reactions, or an empty list if no one has reacted. + + + +```dart +message.reactions +``` + + + +### Check if the Logged-in User Has Reacted + +Use the `reactedByMe` property on any `ReactionCount` object to check whether the logged-in user has reacted with that particular emoji. Returns `true` if they have, `false` otherwise. + + + +```dart +for (ReactionCount reactionCount in message.reactions) { + debugPrint("isReactedByMe ${reactionCount.reactedByMe}"); //Return true is logged-in user reacted on that message, otherwise false +} +``` + + + +## Fetch Reactions for a Message + +To get the full list of who reacted with what on a specific message, use `ReactionsRequestBuilder`. You can paginate through results with `fetchNext()` and `fetchPrevious()` (max 100 per request). + +| Builder Property | Type | Description | +| --- | --- | --- | +| `messageId` | `int?` | The message ID to fetch reactions for. Required. | +| `reaction` | `String?` | Filter to a specific emoji (e.g., `"\U0001f60a"`). When set, only reactions matching this emoji are returned. | +| `limit` | `int` | Number of reactions to fetch per request. Default is `10`. | + +### Fetch Next + +The `fetchNext()` method fetches the next set of reactions for the message. + + + +```dart +int messageId = 1; + +ReactionsRequest reactionsRequest = (ReactionsRequestBuilder() + ..limit = 30 + ..messageId = messageId +).build(); + +reactionsRequest.fetchNext( + onSuccess: (reactions) { + for (Reaction reaction in reactions) { + debugPrint("Success: $reaction"); + } + }, + onError: (e) { + debugPrint("Error: ${e.message}"); + }, +); +``` + + + +The `fetchNext()` method returns a `List` representing individual user reactions on the message. + + +**On Success** — A list of `Reaction` objects for the message: + + + +**Reaction Object (per item in list):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | string | Unique reaction identifier | `"r1"` | +| `messageId` | number | ID of the message this reaction belongs to | `1` | +| `reaction` | string | The reaction emoji | `"\U0001f634"` | +| `uid` | string | UID of the user who reacted | `"cometchat-uid-1"` | +| `reactedAt` | number | Epoch timestamp when the reaction was added | `1745554729` | +| `reactedBy` | object | User who added the reaction | [See below \u2193](#fetch-next-reaction-reactedby-object) | + +--- + + + +**`reactedBy` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +### Fetch Previous + +The `fetchPrevious()` method fetches the previous set of reactions for the message. + + + +```dart +int messageId = 1; + +ReactionsRequest reactionsRequest = (ReactionsRequestBuilder() + ..limit = 30 + ..messageId = messageId +).build(); + +reactionsRequest.fetchPrevious( + onSuccess: (reactions) { + for (Reaction reaction in reactions) { + debugPrint("Success: $reaction"); + } + }, + onError: (e) { + debugPrint("Error: ${e.message}"); + }, +); +``` + + + + +**On Success** — A list of `Reaction` objects for the message: + + + +**Reaction Object (per item in list):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | string | Unique reaction identifier | `"r1"` | +| `messageId` | number | ID of the message this reaction belongs to | `1` | +| `reaction` | string | The reaction emoji | `"\U0001f634"` | +| `uid` | string | UID of the user who reacted | `"cometchat-uid-1"` | +| `reactedAt` | number | Epoch timestamp when the reaction was added | `1745554729` | +| `reactedBy` | object | User who added the reaction | [See below \u2193](#fetch-previous-reaction-reactedby-object) | + +--- + + + +**`reactedBy` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Real-Time Reaction Events + +Register a `MessageListener` to receive reaction events as they happen. This is how you keep your UI in sync when other users add or remove reactions. + + + +```dart +class MyClass with MessageListener { + + String listenerID = "UNIQUE_LISTENER_ID"; + + MyClass() { + CometChat.addMessageListener(listenerID, this); + } + + @override + void onMessageReactionAdded(ReactionEvent reactionEvent) { + debugPrint("Reaction added ${reactionEvent.reaction}"); + } + + @override + void onMessageReactionRemoved(ReactionEvent reactionEvent) { + debugPrint("Reaction removed ${reactionEvent.reaction}"); + } +} +``` + + + +Each reaction listener callback receives a `ReactionEvent` object containing: + +| Parameter | Type | Description | +| --- | --- | --- | +| `reaction` | `Reaction?` | The reaction that was added or removed. | +| `receiverId` | `String?` | The UID or GUID of the receiver. | +| `receiverType` | `String?` | The type of the receiver (`"user"` or `"group"`). | +| `conversationId` | `String?` | The unique conversation identifier. | +| `parentMessageId` | `int?` | The ID of the parent message (for threaded messages). | + +### Remove the Listener + + + +```dart +String listenerID = "UNIQUE_LISTENER_ID"; + +CometChat.removeMessageListener(listenerID); +``` + + + + +Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. + + +## Update a Message with Reaction Info + +When you receive a real-time reaction event, you'll want to update the corresponding message object so your UI reflects the change. `CometChatHelper.updateMessageWithReactionInfo()` does this — it takes the `BaseMessage` instance, the `Reaction` event data, and the action type, then returns the updated message. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `baseMessage` | `BaseMessage` | The message object to update. | +| `messageReaction` | `Reaction` | The reaction event received from the listener. | +| `action` | `String` | `ReactionAction.reactionAdded` or `ReactionAction.reactionRemoved` | + + + +```dart +// Pass the message and Reaction from your listener (e.g. onMessageReactionAdded) +Future refreshMessageWithReaction( + BaseMessage message, + Reaction messageReaction, +) async { + // The received reaction event action type: reactionAdded or reactionRemoved + var action = ReactionAction.reactionAdded; + + BaseMessage? modifiedBaseMessage = + await CometChatHelper.updateMessageWithReactionInfo( + message, + messageReaction, + action, + ); + // Use modifiedBaseMessage?.reactions to refresh your UI +} +``` + + + +After calling this method, use `modifiedBaseMessage?.reactions` to get the latest reactions and refresh your UI. + +--- + +## Next Steps + + + + Send text, media, and custom messages to users and groups + + + Listen for incoming messages in real-time and fetch missed messages + + + Create and manage message threads + + + Mention users and groups in messages + + diff --git a/sdk/flutter/v4/real-time-listeners.mdx b/sdk/flutter/v4/real-time-listeners.mdx new file mode 100644 index 000000000..0ba1a4508 --- /dev/null +++ b/sdk/flutter/v4/real-time-listeners.mdx @@ -0,0 +1,639 @@ +--- +title: "All Real Time Listeners" +sidebarTitle: "Real-Time Listeners" +description: "Learn how to register and manage real-time event listeners for messages, users, groups, calls, connections, login, and AI in the CometChat Flutter SDK." +--- + + + +```dart +// Message Listener - receive messages in real-time +CometChat.addMessageListener("message_listener", MessageListener( + onTextMessageReceived: (TextMessage message) { + debugPrint("Text received: ${message.text}"); + }, + onMediaMessageReceived: (MediaMessage message) { + debugPrint("Media received: ${message.attachment?.fileUrl}"); + }, +)); + +// User Listener - track user online/offline status +CometChat.addUserListener("user_listener", UserListener( + onUserOnline: (User user) { + debugPrint("${user.name} is online"); + }, + onUserOffline: (User user) { + debugPrint("${user.name} is offline"); + }, +)); + +// Group Listener - track group membership changes +CometChat.addGroupListener("group_listener", GroupListener( + onGroupMemberJoined: (action, joinedUser, joinedGroup) { + debugPrint("${joinedUser.name} joined ${joinedGroup.name}"); + }, + onGroupMemberLeft: (action, leftUser, leftGroup) { + debugPrint("${leftUser.name} left ${leftGroup.name}"); + }, +)); + +// Call Listener - track incoming/outgoing calls +CometChat.addCallListener("call_listener", CallListener( + onIncomingCallReceived: (Call call) { + debugPrint("Incoming call from: ${call.sender?.name}"); + }, + onOutgoingCallAccepted: (Call call) { + debugPrint("Call accepted"); + }, +)); + +// Connection Listener - monitor WebSocket connection +CometChat.addConnectionListener("conn_listener", ConnectionListener( + onConnected: () => debugPrint("Connected"), + onDisconnected: () => debugPrint("Disconnected"), +)); + +// Remove listeners when no longer needed +CometChat.removeMessageListener("message_listener"); +CometChat.removeUserListener("user_listener"); +CometChat.removeGroupListener("group_listener"); +CometChat.removeCallListener("call_listener"); +CometChat.removeConnectionListener("conn_listener"); +``` + + +CometChat provides real-time event listeners to keep your app updated with live changes. These listeners notify your app when messages are received, users come online/offline, group membership changes occur, calls are initiated, and connection state changes. + + +**Listener Cleanup Required:** Always remove listeners when they're no longer needed (e.g., on widget dispose or page navigation). Failing to remove listeners can cause memory leaks and duplicate event handling. + + +CometChat provides 7 listener types: + +1. [MessageListener](#message-listener) +2. [UserListener](#user-listener) +3. [GroupListener](#group-listener) +4. [CallListener](#call-listener) +5. [ConnectionListener](#connection-listener) +6. [LoginListener](#login-listener) +7. [AIAssistantListener](#ai-assistant-listener) + +## Message Listener + +The `MessageListener` class provides you with live events related to messages. Below are the callback methods provided by the `MessageListener` class. + +### MessageListener Events + +| Method | Parameter Type | Description | +| ------ | -------------- | ----------- | +| `onTextMessageReceived(TextMessage message)` | [`TextMessage`](/sdk/reference/messages#textmessage) | Triggered when a text message is received | +| `onMediaMessageReceived(MediaMessage message)` | [`MediaMessage`](/sdk/reference/messages#mediamessage) | Triggered when a media message is received | +| `onCustomMessageReceived(CustomMessage message)` | [`CustomMessage`](/sdk/reference/messages#custommessage) | Triggered when a custom message is received | +| `onTypingStarted(TypingIndicator typingIndicator)` | [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) | Triggered when a user starts typing in a user/group conversation | +| `onTypingEnded(TypingIndicator typingIndicator)` | [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) | Triggered when a user stops typing in a user/group conversation | +| `onMessagesDelivered(MessageReceipt messageReceipt)` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are marked as delivered for a conversation | +| `onMessagesRead(MessageReceipt messageReceipt)` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are marked as read for a conversation | +| `onMessagesDeliveredToAll(MessageReceipt messageReceipt)` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are delivered to all participants | +| `onMessagesReadByAll(MessageReceipt messageReceipt)` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are read by all participants | +| `onMessageEdited(BaseMessage message)` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is edited in a user/group conversation | +| `onMessageDeleted(BaseMessage message)` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is deleted in a user/group conversation | +| `onMessageModerated(BaseMessage message)` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is moderated | +| `onTransientMessageReceived(TransientMessage message)` | [`TransientMessage`](/sdk/reference/auxiliary#transientmessage) | Triggered when a transient message is received | +| `onInteractiveMessageReceived(InteractiveMessage message)` | `InteractiveMessage` | Triggered when an interactive message is received | +| `onInteractionGoalCompleted(InteractionReceipt receipt)` | `InteractionReceipt` | Triggered when an interaction goal is achieved | +| `onMessageReactionAdded(ReactionEvent reactionEvent)` | `ReactionEvent` | Triggered when a reaction is added to a message | +| `onMessageReactionRemoved(ReactionEvent reactionEvent)` | `ReactionEvent` | Triggered when a reaction is removed from a message | +| `onAIAssistantMessageReceived(AIAssistantMessage message)` | `AIAssistantMessage` | Triggered when an AI assistant message is received | +| `onAIToolResultReceived(AIToolResultMessage message)` | `AIToolResultMessage` | Triggered when an AI tool result message is received | +| `onAIToolArgumentsReceived(AIToolArgumentMessage message)` | `AIToolArgumentMessage` | Triggered when AI tool arguments are received | + +To add the `MessageListener`, you need to use the `addMessageListener()` method provided by the `CometChat` class. + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | +| `listener` | `MessageListener` | An instance of a class that implements the `MessageListener` mixin | + + + +```dart +class Class_Name with MessageListener { + + //CometChat.addMessageListener("listenerId", this); + @override + void onTextMessageReceived(TextMessage textMessage) { + + } + + @override + void onMediaMessageReceived(MediaMessage mediaMessage) { + + } + + @override + void onCustomMessageReceived(CustomMessage customMessage) { + + } + + @override + void onTypingStarted(TypingIndicator typingIndicator) { + + } + + @override + void onTypingEnded(TypingIndicator typingIndicator) { + + } + + + @override + void onMessagesDelivered(MessageReceipt messageReceipt) { + + } + + @override + void onMessagesRead(MessageReceipt messageReceipt) { + + } + + @override + void onMessageEdited(BaseMessage message) { + + } + + @override + void onMessageDeleted(BaseMessage message) { + + } + + @override + void onInteractionGoalCompleted(InteractionReceipt receipt) { + + + } + + @override + void onInteractiveMessageReceived(InteractiveMessage message) { + + } + + + @Override + public void onTransientMessageReceived(TransientMessage transientMessage) { + + } + + @Override + public void onMessageReactionAdded(ReactionEvent reactionEvent) { + + } + + @Override + public void onMessageReactionRemoved(ReactionEvent reactionEvent) { + + } + +} +``` + + + + + +where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. + +Once the activity/fragment where the `MessageListener` is declared is not in use, you need to remove the listener using the `removeMessageListener()` method which takes the id of the listener to be removed as the parameter. We suggest you call this method in the `onPause()` method of the activity/fragment. + + + +```dart +CometChat.removeMessageListener(UNIQUE_LISTENER_ID) +``` + + + +## User Listener + +The `UserListener` class provides you with live events related to users. Below are the callback methods provided by the `UserListener` class. + +### UserListener Events + +| Method | Parameter Type | Description | +| ------ | -------------- | ----------- | +| `onUserOnline(User user)` | [`User`](/sdk/reference/entities#user) | Triggered when a user comes online and is available to chat. The details of the user can be obtained from the `User` object received as the method parameter. | +| `onUserOffline(User user)` | [`User`](/sdk/reference/entities#user) | Triggered when a user goes offline. The details of the user can be obtained from the `User` object received as the parameter. | + +To add the `UserListener`, you need to use the `addUserListener()` method provided by the `CometChat` class. + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | +| `listener` | `UserListener` | An instance of a class that implements the `UserListener` mixin | + + + +```dart +class Class_Name with UserListener { + //CometChat.addUserListener("user_Listener_id", this); + @override + void onUserOnline(User user) { + + } + + @override + void onUserOffline(User user) { + + } + +} +``` + + + + + +where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. + +Once the activity/fragment where the `UserListener` is declared is not in use, you need to remove the listener using the `removeUserListener()` method which takes the id of the listener to be removed as the parameter. We suggest you call this method in the `onPause()` method of the activity/fragment. + + + +```dart +CometChat.removeUserListener(UNIQUE_LISTENER_ID) +``` + + + + + +## Group Listener + +The `GroupListener` class provides you with all the real-time events related to groups. Below are the callback methods provided by the `GroupListener` class. + +### GroupListener Events + +| Method | Parameter Types | Description | +| ------ | --------------- | ----------- | +| `onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user joins any group. All the members present in the group will receive this event. | +| `onGroupMemberLeft(Action action, User leftUser, Group leftGroup)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user who was a member of any group leaves the group. All the members of the group receive this event. | +| `onGroupMemberKicked(Action action, User kickedUser, User kickedBy, Group kickedFrom)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user is kicked from a group. All the members including the user receive this event. | +| `onGroupMemberBanned(Action action, User bannedUser, User bannedBy, Group bannedFrom)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user is banned from a group. All the members including the user receive this event. | +| `onGroupMemberUnbanned(Action action, User unbannedUser, User unbannedBy, Group unbannedFrom)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user is unbanned from a group. All the members of the group receive this event. | +| `onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), `String`, `String`, [`Group`](/sdk/reference/entities#group) | Triggered when the scope of any group member has been changed. All the members that are a part of that group receive this event. | +| `onMemberAddedToGroup(Action action, User addedBy, User userAdded, Group addedTo)` | [`Action`](/sdk/reference/messages#action), [`User`](/sdk/reference/entities#user), [`User`](/sdk/reference/entities#user), [`Group`](/sdk/reference/entities#group) | Triggered when a user is added to any group. All the members including the user himself receive this event. | + +To add the `GroupListener`, you need to use the `addGroupListener()` method provided by the `CometChat` class. + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | +| `listener` | `GroupListener` | An instance of a class that implements the `GroupListener` mixin | + + + +```dart +class Class_Name with GroupListener { + //CometChat.addGroupListener("UNIQUE_LISTENER_ID", this); + + @override + void onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup) { + + } + + @override + void onGroupMemberLeft(Action action, User leftUser, Group leftGroup) { + + } + + @override + void onGroupMemberKicked(Action action, User kickedUser, User kickedBy, Group kickedFrom) { + + } + + @override + void onGroupMemberBanned(Action action, User bannedUser, User bannedBy, Group bannedFrom) { + + } + + @override + void onGroupMemberUnbanned(Action action, User unbannedUser, User unbannedBy, Group unbannedFrom) { + + } + + @override + void onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group) { + + } + + @override + void onMemberAddedToGroup(Action action, User addedby, User userAdded, Group addedTo) { + + } + +} +``` + + + + + +where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. + +Once the activity/fragment where the `GroupListener` is declared is not in use, you need to remove the listener using the `removeGroupListener()` method which takes the id of the listener to be removed as the parameter. We suggest you call this method in the `onPause()` method of the activity/fragment. + + + +```dart +CometChat.removeGroupListener(UNIQUE_LISTENER_ID) +``` + + + + + +## Call Listener + +The `CallListener` class provides you with real-time events related to calls. Below are the callback methods provided by the `CallListener` class. + +### CallListener Events + +| Method | Parameter Type | Description | +| ------ | -------------- | ----------- | +| `onIncomingCallReceived(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when an incoming call is received | +| `onOutgoingCallAccepted(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when an outgoing call is accepted by the receiver | +| `onOutgoingCallRejected(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when an outgoing call is rejected by the receiver | +| `onIncomingCallCancelled(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when an incoming call is cancelled by the caller | +| `onCallEndedMessageReceived(Call call)` | [`Call`](/sdk/reference/messages#call) | Triggered when a call ended message is received | + +To add the `CallListener`, you need to use the `addCallListener()` method provided by the `CometChat` class. + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | +| `listener` | `CallListener` | An instance of a class that implements the `CallListener` mixin | + + + +```dart +class Class_Name with CallListener { + //CometChat.addCallListener("UNIQUE_LISTENER_ID", this); + + @override + void onIncomingCallReceived(Call call) { + + } + + @override + void onOutgoingCallAccepted(Call call) { + + } + + @override + void onOutgoingCallRejected(Call call) { + + } + + @override + void onIncomingCallCancelled(Call call) { + + } + + @override + void onCallEndedMessageReceived(Call call) { + + } + +} +``` + + + + + +where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. + +Once the `CallListener` is no longer needed, remove it using the `removeCallListener()` method. + + + +```dart +CometChat.removeCallListener(UNIQUE_LISTENER_ID) +``` + + + + + +## Connection Listener + +The `ConnectionListener` class provides you with real-time events related to the WebSocket connection status. Below are the callback methods provided by the `ConnectionListener` class. + +### ConnectionListener Events + +| Method | Parameter Type | Description | +| ------ | -------------- | ----------- | +| `onConnected()` | — | Triggered when the SDK successfully establishes a connection to the WebSocket server | +| `onConnecting()` | — | Triggered when the SDK is attempting to establish a connection to the WebSocket server | +| `onDisconnected()` | — | Triggered when the SDK gets disconnected due to network fluctuations or other issues | +| `onFeatureThrottled()` | — | Triggered when CometChat automatically toggles off certain features to prevent performance loss | +| `onConnectionError(CometChatException error)` | `CometChatException` | Triggered when an error occurs while maintaining the WebSocket connection | + +To add the `ConnectionListener`, you need to use the `addConnectionListener()` method provided by the `CometChat` class. + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | +| `listener` | `ConnectionListener` | An instance of a class that implements the `ConnectionListener` mixin | + + + +```dart +class Class_Name with ConnectionListener { + //CometChat.addConnectionListener("UNIQUE_LISTENER_ID", this); + + @override + void onConnected() { + + } + + @override + void onConnecting() { + + } + + @override + void onDisconnected() { + + } + + @override + void onFeatureThrottled() { + + } + + @override + void onConnectionError(CometChatException error) { + + } + +} +``` + + + + + +where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. + +Once the `ConnectionListener` is no longer needed, remove it using the `removeConnectionListener()` method. + + + +```dart +CometChat.removeConnectionListener(UNIQUE_LISTENER_ID) +``` + + + + + +## Login Listener + +The `LoginListener` class provides you with real-time events related to user authentication. Below are the callback methods provided by the `LoginListener` class. + +### LoginListener Events + +| Method | Parameter Type | Description | +| ------ | -------------- | ----------- | +| `loginSuccess(User user)` | [`User`](/sdk/reference/entities#user) | Triggered when a user successfully logs in | +| `loginFailure(CometChatException e)` | `CometChatException` | Triggered when a login attempt fails | +| `logoutSuccess()` | — | Triggered when a user successfully logs out | +| `logoutFailure(CometChatException e)` | `CometChatException` | Triggered when a logout attempt fails | + +To add the `LoginListener`, you need to use the `addLoginListener()` method provided by the `CometChat` class. + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | +| `listener` | `LoginListener` | An instance of a class that implements the `LoginListener` mixin | + + + +```dart +class Class_Name with LoginListener { + //CometChat.addLoginListener("UNIQUE_LISTENER_ID", this); + + @override + void loginSuccess(User user) { + + } + + @override + void loginFailure(CometChatException e) { + + } + + @override + void logoutSuccess() { + + } + + @override + void logoutFailure(CometChatException e) { + + } + +} +``` + + + + + +where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. + +Once the `LoginListener` is no longer needed, remove it using the `removeLoginListener()` method. + + + +```dart +CometChat.removeLoginListener(UNIQUE_LISTENER_ID) +``` + + + + + +## AI Assistant Listener + +The `AIAssistantListener` class provides you with real-time events related to AI assistant interactions. Below are the callback methods provided by the `AIAssistantListener` class. + +### AIAssistantListener Events + +| Method | Parameter Type | Description | +| ------ | -------------- | ----------- | +| `onAIAssistantEventReceived(AIAssistantBaseEvent aiAssistantBaseEvent)` | [`AIAssistantBaseEvent`](/sdk/reference/messages#aiassistantbaseevent) | Triggered when an AI assistant event is received | + +To add the `AIAssistantListener`, you need to use the `addAIAssistantListener()` method provided by the `CometChat` class. + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `listenerId` | `String` | A unique identifier for the listener. No two listeners should share the same ID. | +| `listener` | `AIAssistantListener` | An instance of a class that implements the `AIAssistantListener` mixin | + + + +```dart +class Class_Name with AIAssistantListener { + //CometChat.addAIAssistantListener("UNIQUE_LISTENER_ID", this); + + @override + void onAIAssistantEventReceived(AIAssistantBaseEvent aiAssistantBaseEvent) { + + } + +} +``` + + + + + +where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. + +Once the `AIAssistantListener` is no longer needed, remove it using the `removeAIAssistantListener()` method. + + + +```dart +CometChat.removeAIAssistantListener(UNIQUE_LISTENER_ID) +``` + + + + + +--- + +## Next Steps + + + + Handle incoming messages in real-time using message listeners + + + Track when users come online or go offline + + + Show real-time typing status in conversations + + + Track message delivery and read status + + + Monitor SDK connection to CometChat servers + + + Monitor user login and logout events + + diff --git a/sdk/flutter/v4/receive-messages.mdx b/sdk/flutter/v4/receive-messages.mdx new file mode 100644 index 000000000..f4f376439 --- /dev/null +++ b/sdk/flutter/v4/receive-messages.mdx @@ -0,0 +1,1410 @@ +--- +title: "Receive A Message" +sidebarTitle: "Receive Messages" +description: "Learn how to receive real-time messages and fetch missed or historical messages using the CometChat Flutter SDK." +--- + + + +| Field | Value | +| --- | --- | +| Key Classes | `MessageListener`, `MessagesRequest`, `MessagesRequestBuilder` | +| Key Methods | `addMessageListener()`, `removeMessageListener()`, `fetchPrevious()`, `fetchNext()`, `getLastDeliveredMessageId()`, `getUnreadMessageCount()` | +| Listener Events | `onTextMessageReceived`, `onMediaMessageReceived`, `onCustomMessageReceived`, `onTypingStarted`, `onTypingEnded`, `onMessagesDelivered`, `onMessagesRead`, `onMessageEdited`, `onMessageDeleted`, `onInteractiveMessageReceived`, `onMessageReactionAdded`, `onMessageReactionRemoved` | +| Prerequisites | SDK initialized, user logged in | + + + +Receiving messages with CometChat has two parts: + +1. Adding a listener to receive [Real-time Messages](/sdk/flutter/receive-messages#real-time-messages) when your app is running +2. Calling a method to retrieve [Missed Messages](/sdk/flutter/receive-messages#missed-messages) when your app was not running + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Real-time Messages + +*In other words, as a recipient, how do I receive messages when my app is running?* + +For every activity or fragment you wish to receive messages in, you need to register the `MessageListener` using the `addMessageListener()` method. + +We suggest adding the listener in the `init` method of the Stateful class or at the initialization of class where you wish to receive these events in. + + + +```dart +class Class_Name with MessageListener { + +//CometChat.addMessageListener("listenerId", this); + @override +void onTextMessageReceived(TextMessage textMessage) { + debugPrint("Text message received successfully: $textMessage"); +} + +@override +void onMediaMessageReceived(MediaMessage mediaMessage) { + debugPrint("Media message received successfully: $mediaMessage"); +} + +@override +void onCustomMessageReceived(CustomMessage customMessage) { + debugPrint("Custom message received successfully: $customMessage"); +} + +@override +onInteractiveMessageReceived(InteractiveMessage message) { + +} + + +} +``` + + + + + +| Parameter | Description | +| ---------- | ---------------------------------------------------------------------------------------------- | +| listenerID | An ID that uniquely identifies that listener. We recommend using the activity or fragment name | + +### MessageListener Events + +The `MessageListener` mixin provides the following event callbacks. All events are verified against the Flutter SDK source. + +| Event | Parameter Type | Description | +| --- | --- | --- | +| `onTextMessageReceived` | [`TextMessage`](/sdk/reference/messages#textmessage) | Triggered when a text message is received | +| `onMediaMessageReceived` | [`MediaMessage`](/sdk/reference/messages#mediamessage) | Triggered when a media message (image, video, audio, file) is received | +| `onCustomMessageReceived` | [`CustomMessage`](/sdk/reference/messages#custommessage) | Triggered when a custom message is received | +| `onTypingStarted` | [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) | Triggered when a user starts typing | +| `onTypingEnded` | [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) | Triggered when a user stops typing | +| `onMessagesDelivered` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are delivered to the recipient | +| `onMessagesRead` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are read by the recipient | +| `onMessagesDeliveredToAll` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are delivered to all group members | +| `onMessagesReadByAll` | [`MessageReceipt`](/sdk/reference/auxiliary#messagereceipt) | Triggered when messages are read by all group members | +| `onMessageEdited` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is edited | +| `onMessageDeleted` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is deleted | +| `onTransientMessageReceived` | [`TransientMessage`](/sdk/reference/auxiliary#transientmessage) | Triggered when a transient (non-persistent) message is received | +| `onInteractiveMessageReceived` | `InteractiveMessage` | Triggered when an interactive message is received | +| `onInteractionGoalCompleted` | `InteractionReceipt` | Triggered when an interaction goal is completed | +| `onMessageReactionAdded` | `ReactionEvent` | Triggered when a reaction is added to a message | +| `onMessageReactionRemoved` | `ReactionEvent` | Triggered when a reaction is removed from a message | +| `onMessageModerated` | [`BaseMessage`](/sdk/reference/messages#basemessage) | Triggered when a message is moderated | +| `onAIAssistantMessageReceived` | `AIAssistantMessage` | Triggered when an AI assistant message is received | +| `onAIToolResultReceived` | `AIToolResultMessage` | Triggered when an AI tool result is received | +| `onAIToolArgumentsReceived` | `AIToolArgumentMessage` | Triggered when AI tool arguments are received | + +We recommend you remove the listener once the activity or fragment is not in use. Typically, this can be added in the `dispose()` method. + + +Always remove message listeners when they're no longer needed (e.g., in the `dispose()` method of your StatefulWidget). Failing to remove listeners can cause memory leaks and duplicate event handling. + + + + +```dart +private String listenerID = "UNIQUE_LISTENER_ID"; + +CometChat.removeMessageListener(listenerID); +``` + + + + + + + +As a sender, you will not receive your own message in a real-time message event. However, if a user is logged-in using multiple devices, they will receive an event for their own message in other devices. + + + +## Missed Messages + +*In other words, as a recipient, how do I receive messages that I missed when my app was not running?* + +For most use cases, you will need to add functionality to load missed messages. If you're building an on-demand or live streaming app, you may choose to skip this and fetch the message history (say, last 100 messages) instead. + +Using the same `MessagesRequest` class and the filters provided by the `MessagesRequestBuilder` class, you can fetch the message that we sent to the logged-in user but were not delivered to them as they were offline. For this, you will require the ID of the last message received. You can either maintain it at your end or use the `getLastDeliveredMessageId()` method provided by the CometChat class. This ID needs to be passed to the `setMessageId()` method of the builder class. + +Now using the `fetchNext()` method, you can fetch all the messages that were sent to the user when they were offline. + +Calling the `fetchNext()` method on the same object repeatedly allows you to fetch all the offline messages for the logged in user in a paginated manner + +### For a Particular One-on-one Conversation + + + +```dart +int limit = 30; +int lastMessageId = -1; +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..limit = limit + ..messageId = lastMessageId + ).build(); + +messageRequest.fetchNext(onSuccess: (List list) { + for (BaseMessage message in list) { + if(message is TextMessage){ + debugPrint("Text message received successfully: "+(message as TextMessage).toString()); + }else if(message is MediaMessage){ + debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); + } + } + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `501` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-missed-user-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-missed-user-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +### For a Particular Group Conversation + + + +```dart +int limit = 30; +int lastMessageId = -1; +String GUID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..guid = GUID + ..limit = limit + ..messageId = lastMessageId + ).build(); + +messageRequest.fetchNext(onSuccess: (List list) { + for (BaseMessage message in list) { + if(message is TextMessage){ + debugPrint("Text message received successfully: "+(message as TextMessage).toString()); + }else if(message is MediaMessage){ + debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); + } + } + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `502` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver group object | [See below ↓](#fetch-missed-group-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"group_cometchat-uid-1"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | GUID of the receiver group | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-missed-group-sender-object) | +| `receiverType` | string | Type of the receiver | `"group"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Unread Messages + +*In other words, as a logged-in user, how do I fetch the messages I've not read?* + +Using the `MessagesRequest` class described above, you can fetch the unread messages for the logged-in user. In order to achieve this, you need to set the `unread` variable in the builder to `true` so that only the unread messages are fetched. + +### For a Particular One-on-one Conversation + + + +```dart +int limit = 30; +int lastMessageId = -1; +String GUID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..guid = GUID + ..limit = limit + ..messageId = lastMessageId + ..unread = true + ).build(); + +messageRequest.fetchPrevious(onSuccess: (List list) { + for (BaseMessage message in list) { + if(message is TextMessage){ + debugPrint("Text message received successfully: "+(message as TextMessage).toString()); + }else if(message is MediaMessage){ + debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); + } + } + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `503` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-unread-user-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-unread-user-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +### For a Particular Group Conversation + + + +```dart +int limit = 30; +int lastMessageId = -1; +String GUID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..guid = GUID + ..limit = limit + ..messageId = lastMessageId + ..unread = true + ).build(); + +messageRequest.fetchPrevious(onSuccess: (List list) { + for (BaseMessage message in list) { + if(message is TextMessage){ + debugPrint("Text message received successfully: "+(message as TextMessage).toString()); + }else if(message is MediaMessage){ + debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); + } + } + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `504` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-unread-group-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"group_cometchat-uid-1"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | GUID of the receiver group | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-unread-group-sender-object) | +| `receiverType` | string | Type of the receiver | `"group"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + + +Base Message + +The list of messages received is in the form of objects of [`BaseMessage`](/sdk/reference/messages#basemessage) class. A `BaseMessage` can either be an object of the [`TextMessage`](/sdk/reference/messages#textmessage), [`MediaMessage`](/sdk/reference/messages#mediamessage), [`CustomMessage`](/sdk/reference/messages#custommessage), [`Action`](/sdk/reference/messages#action) or [`Call`](/sdk/reference/messages#call) class. You can use the `is` operator to check the type of object. + + + +## Message History + +*In other words, as a logged-in user, how do I fetch the message history for a user or a group conversation?* + +### For a Particular One-on-one Conversation + +Using the `MessagesRequest` class and the filters for the `MessagesRequestBuilder` class as shown in the below code snippet, you can fetch the entire conversation between the logged in user and any particular user. For this use case, it is mandatory to set the UID parameter of the builder. This UID is the unique id of the user for which the conversation needs to be fetched. + + + +```dart +int limit = 30; +int lastMessageId = -1; +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..limit = limit + ).build(); + +messageRequest.fetchPrevious(onSuccess: (List list) { + for (BaseMessage message in list) { + if(message is TextMessage){ + debugPrint("Text message received successfully: "+(message as TextMessage).toString()); + }else if(message is MediaMessage){ + debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); + } + } + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `505` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-history-user-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-history-user-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +Calling the `fetchPrevious()` method on the same object repeatedly allows you to fetch all the previous messages in a paginated way. + +### For a Particular Group Conversation + +Using the `MessagesRequest` class and the filters for the `MessagesRequestBuilder` class as shown in the below code snippet, you can fetch the entire conversation for any group provided you have joined the group. For this use case, it is mandatory to set the GUID parameter of the builder. This GUID is the unique identifier of the Group for which the messages are to be fetched. + + + +```dart +int limit = 30; +String GUID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..guid = GUID + ..limit = limit + ).build(); + +messageRequest.fetchPrevious(onSuccess: (List list) { + for (BaseMessage message in list) { + if(message is TextMessage){ + debugPrint("Text message received successfully: "+(message as TextMessage).toString()); + }else if(message is MediaMessage){ + debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); + } + } + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `506` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-history-group-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"group_cometchat-uid-1"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | GUID of the receiver group | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-history-group-sender-object) | +| `receiverType` | string | Type of the receiver | `"group"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +Calling the `fetchPrevious()` method on the same object repeatedly allows you to fetch the entire conversation between the logged in user and the specified user. This can be implemented with upward scrolling to display the entire conversation. + +## Search Messages + +In other words, as a logged-in user, how do I search a message? + +In order to do this, you can use the `searchKeyword` method. This method accepts string as input. When set, the SDK will fetch messages which match the `searchKeyword`. + + + +By default, the searchKey is searched only in message text. However, if you enable `Conversation & Advanced Search`, the searchKey will be searched in message text, file name, mentions & mime type of the file. + +The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + + +| Feature | Basic Search | Advance Search | +| ---------------- | ------------ | -------------- | +| Text search | ✅ | ✅ | +| File name search | ❌ | ✅ | +| Mentions search | ❌ | ✅ | +| Mime type search | ❌ | ✅ | + +### For a Particular One-on-one Conversation + + + +```dart +int limit = 30; +int searchKeyword = "search keyboard"; +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..limit = limit + .searchKeyword=searchKeyword + ).build(); + +messageRequest.fetchPrevious(onSuccess: (List list) { + for (BaseMessage message in list) { + if(message is TextMessage){ + debugPrint("Text message received successfully: "+(message as TextMessage).toString()); + }else if(message is MediaMessage){ + debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); + } + } + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `507` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-search-user-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-search-user-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +### For a Particular Group Conversation + + + +```dart +int limit = 30; +int searchKeyword = "search keyboard"; +String GUID = "cometchat-guid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..guid = GUID + ..limit = limit + .searchKeyword=searchKeyword + ).build(); + +messageRequest.fetchPrevious(onSuccess: (List list) { + for (BaseMessage message in list) { + if(message is TextMessage){ + debugPrint("Text message received successfully: "+(message as TextMessage).toString()); + }else if(message is MediaMessage){ + debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); + } + } + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched messages (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `508` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver group object | [See below ↓](#fetch-search-group-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"group_cometchat-guid-1"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | GUID of the receiver group | `"cometchat-guid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-search-group-sender-object) | +| `receiverType` | string | Type of the receiver | `"group"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Unread Messages Count + +*In other words, as a logged-in user, how do I find out the number of unread messages that I have?* + +### For a Particular One-on-one Conversation + +*In other words, how do I find out the number of unread messages I have from a particular user?* + +In order to get the unread message count for a particular user (with respect to the logged-in user), you can use the `getUnreadMessageCountForUser()`. + +This method has the two variants: + + + +```dart +CometChat.getUnreadMessageCountForUser(String UID, Callbacks); +``` + + + + + +If you wish to ignore the messages from blocked users you can use the below syntax setting the boolean parameter to `true`: + + + +```dart +CometChat.getUnreadMessageCountForUser(String UID, boolean hideMessagesFromBlockedUsers, Callbacks); +``` + + + + + + + +```dart +private String UID = "cometchat-uid-1" + +CometChat.getUnreadMessageCountForUser(UID, new CometChat.CallbackListener>() { +@Override + public void onSuccess(HashMap stringIntegerHashMap) { + // handle success +} + +@Override + public void onError(CometChatException e) { + // handle error +} +}); +``` + + + + + +In the `onSuccess()` callback, you will receive a `Map` which will contain the `UID` of the user as the key and the unread message count as the value. + +### For a Particular Group Conversation + +*In other words, how do I find out the number of unread messages I have in a single group?* + +In order to get the unread message count for a particular group, you can use the `getUnreadMessageCountForGroup()`. + +This method has two variants: + + + +```dart +Map? ab = await CometChat.getUnreadMessageCountForGroup(guid: guid); +``` + + + + + +If you wish to ignore the messages from blocked users you can use the following syntax setting the boolean parameter to `true`: + + + +```dart +Map? ab = await CometChat.getUnreadMessageCountForGroup(guid: guid,hideMessagesFromBlockedUsers: hideBlockedUser); +``` + + + + + + + +```dart +private String GUID = "cometchat-guid-1" + +CometChat.getUnreadMessageCountForGroup(guid: GUID,hideMessagesFromBlockedUsers: hideBlockedUser, + onSuccess: (Map map) { + + }, + onError: (CometChatException e) { + + }); +``` + + + + + +In the `onSuccess()` callback, you will receive a `Map` which will contain the `GUID` of the group as the key and the unread message count as the value. + +### For All One-on-one & Group Conversations + +*In other words, how do I find out the number of unread messages for every one-on-one and group conversation?* + +In order to get all the unread message counts, you can use the `getUnreadMessageCount()` method. This method has two variants: + + + +```dart +await CometChat.getUnreadMessageCount(); +``` + + + + + +If you wish to ignore the messages from blocked users you can use the following syntax setting the boolean parameter to `true`: + + + +```dart +await CometChat.getUnreadMessageCount(hideMessagesFromBlockedUsers: true); +``` + + + + + + + +```dart +CometChat.getUnreadMessageCount(hideMessagesFromBlockedUsers: hideBlockedUser, + onSuccess: (Map> map) { + + },onError: (CometChatException e) { + + }); +``` + + + + + +In the `onSuccess()` callback, you will receive a `Map` having two keys: + +1. `user` - The value for this key holds another `Map` that holds the `UIDs` of users and their corresponding unread message counts. +2. `group` - The value for this key holds another `Map` that holds the `GUIDs` of groups and their corresponding unread message counts. + +### For Unread Count for all One-on-one Conversations + +*In other words, how do I find out the number of unread messages I have for every user?* + +In order to fetch the unread message counts for just the users, you can use the `getUnreadMessageCountForAllUsers()` method. + +This method, just like others, has two variants: + + + +```dart +await CometChat.getUnreadMessageCountForAllUsers(); +``` + + + + + +If you wish to ignore the messages from blocked users you can use the following syntax setting the boolean parameter to `true`: + + + +```dart +await CometChat.getUnreadMessageCountForAllUsers(hideMessagesFromBlockedUsers: true); +``` + + + + + + + +```dart +CometChat.getUnreadMessageCountForAllUsers(hideMessagesFromBlockedUsers: hideMessagesFromBlockedUsers, + onSuccess: (Map map) { + + }, + onError: (CometChatException e) { + + }); +``` + + + + + +In the `onSuccess()` callback, you will receive a `Map` that will contain the `UIDs` of users as the key and the unread message counts as the values. + +### Fetch Unread Count for all Group Conversations + +*In other words, how do I find out the number of unread messages for every group?* + +In order to fetch the unread message counts for all groups, you can use the `getUnreadMessageCountForAllGroups()` method. + +This method has two variants: + + + +```dart +await CometChat.getUnreadMessageCountForAllGroups(); +``` + + + + + +If you wish to ignore the messages from blocked users you can use the below syntax setting the boolean parameter to `true`: + + + +```dart +await CometChat.getUnreadMessageCountForAllGroups(hideMessagesFromBlockedUsers: true); +``` + + + + + + + +```dart +CometChat.getUnreadMessageCountForAllGroups(hideMessagesFromBlockedUsers: hideMessagesFromBlockedUsers, + onSuccess: (Map map) { + + }, + onError: (CometChatException e) { + + }); +``` + + + + + +In the `onSuccess()` callback, you will receive a `Map` which will contain the `GUIDs` of the groups as the key and the unread message counts as the values. + +--- + +## Next Steps + + + + Track when messages are delivered and read by recipients + + + Show real-time typing status in conversations + + + Allow users to edit previously sent messages + + + Remove messages from conversations + + diff --git a/sdk/flutter/v4/retrieve-conversations.mdx b/sdk/flutter/v4/retrieve-conversations.mdx new file mode 100644 index 000000000..cb43d8cc9 --- /dev/null +++ b/sdk/flutter/v4/retrieve-conversations.mdx @@ -0,0 +1,864 @@ +--- +title: "Retrieve Conversations" +sidebarTitle: "Retrieve Conversations" +description: "Fetch, filter, tag, and search conversations using the CometChat Flutter SDK." +--- + +{/* TL;DR for Agents and Quick Reference */} + + +```dart +// Fetch conversations list +ConversationsRequest request = (ConversationsRequestBuilder() + ..limit = 30 +).build(); + +await request.fetchNext( + onSuccess: (List conversations) { + for (Conversation conv in conversations) { + debugPrint("Conversation: ${conv.conversationId}"); + } + }, + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Get a specific conversation +CometChat.getConversation("UID", "user", + onSuccess: (Conversation conv) => debugPrint("Got: ${conv.conversationId}"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Tag a conversation +CometChat.tagConversation("UID", "user", ["archived"], + onSuccess: (Conversation conv) => debugPrint("Tagged: ${conv.conversationId}"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Convert message to conversation +Conversation conversation = CometChat.getConversationFromMessage(message); +``` + + +Conversations provide the last message for every one-on-one and group conversation the logged-in user is part of. Each [`Conversation`](/sdk/reference/entities#conversation) object includes the other participant (user or group), the last message, unread counts, and optional tags. Use this to build a Recent Chats list. + +## Retrieve List of Conversations + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +*In other words, as a logged-in user, how do I retrieve the latest conversations that I've been a part of?* + +To fetch the list of conversations, you can use the `ConversationsRequest` class. To use this class i.e. to create an object of the `ConversationsRequest` class, you need to use the `ConversationsRequestBuilder` class. The `ConversationsRequestBuilder` class allows you to set the parameters based on which the conversations are to be fetched. + +Fetching using this builder will return [`Conversation`](/sdk/reference/entities#conversation) objects. + +### ConversationsRequestBuilder Parameters + +The `ConversationsRequestBuilder` to fetch conversations with various filters. + +| Property | Type | Description | +|----------|------|-------------| +| `limit` | `int?` | Number of conversations to fetch per request. Default is 30, max is 50. | +| `conversationType` | `String?` | Filter by `CometChatConversationType.user` or `CometChatConversationType.group`. If not set, both types are returned. | +| `withUserAndGroupTags` | `bool?` | Include user/group tags in the response. Default is `false`. | +| `withTags` | `bool?` | Include conversation tags in the response. Default is `false`. | +| `tags` | `List?` | Fetch only conversations matching the specified tags. | +| `userTags` | `List?` | Fetch only user conversations where the user has the specified tags. | +| `groupTags` | `List?` | Fetch only group conversations where the group has the specified tags. | +| `includeBlockedUsers` | `bool?` | Include conversations with blocked users. Default is `false`. | +| `withBlockedInfo` | `bool?` | Include blocked user information (`hasBlockedMe`, `blockedByMe`). Default is `false`. | +| `searchKeyword` | `String?` | Search conversations by user or group name. Requires Conversation & Advanced Search. | +| `unread` | `bool?` | Fetch only conversations with unread messages. Requires Conversation & Advanced Search. Default is `false`. | +| `setPage` | `int?` | Fetch conversations from a particular page. | +| `hideAgentic` | `bool?` | Exclude AI agent conversations from results. Default is `false`. | +| `onlyAgentic` | `bool?` | Fetch only AI agent conversations. Default is `false`. | + +### Set Limit + +Set the number of conversations to fetch per request. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 +).build(); +``` + + + + +The default value for `limit` is 30 and the max value is 50. + + +### Set Conversation Type + +Filter by conversation type: `user` for one-on-one or `group` for group conversations. If not set, both types are returned. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..conversationType = CometChatConversationType.user +).build(); +``` + + + +When conversations are fetched successfully, the response will include an array of [`Conversation`](/sdk/reference/entities#conversation) objects filtered by the specified type. + +### With User and Group Tags + +Use `withUserAndGroupTags = true` to include user/group tags in the `Conversation` object. Default is `false`. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..withUserAndGroupTags = true +).build(); +``` + + + +When conversations are fetched successfully, the response will include `tags` arrays on the `conversationWith` objects (user or group). + +### Set User Tags + +Fetch user conversations where the user has specific tags. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..userTags = ["tag1"] +).build(); +``` + + + +When conversations are fetched successfully, the response will include only user conversations where the user has the specified tags. + +### Set Group Tags + +Fetch group conversations where the group has specific tags. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..groupTags = ["tag1"] +).build(); +``` + + + +When conversations are fetched successfully, the response will include only group conversations where the group has the specified tags. + +### With Tags + +Use `withTags = true` to include conversation tags in the response. Default is `false`. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..withTags = true +).build(); +``` + + + +### Set Tags + +Fetch conversations that have specific tags. + + + +```dart +List tags = []; +tags.add("archived"); +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..tags = tags +).build(); +``` + + + +### Include Blocked Users + +Use `includeBlockedUsers = true` to include conversations with users you've blocked. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..includeBlockedUsers = true +).build(); +``` + + + +When conversations are fetched successfully, the response includes conversations with blocked users. To also get blocked info details (`blockedByMe`, `hasBlockedMe`), set `withBlockedInfo` to `true`. + +### With Blocked Info + +Use `withBlockedInfo = true` to include blocked user information in the response. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..withBlockedInfo = true +).build(); +``` + + + +### Search Conversations + +Use `searchKeyword` to search conversations by user or group name. + + + +This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + + + + +```dart + ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..searchKeyword = "Hiking" + ).build(); +``` + + + +When conversations are fetched successfully, the response includes conversations where the user or group name matches the search keyword. + +### Unread Conversations + +Use `unread = true` to fetch only conversations with unread messages. + + + +This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) + + + + + +```dart + ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..unread = true + ).build(); +``` + + + +When conversations are fetched successfully, the response includes only conversations with unread messages (`unreadMessageCount` > 0). + +### Hide Agentic Conversations + +Use `hideAgentic = true` to exclude AI agent conversations from the list. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..hideAgentic = true +).build(); +``` + + + +### Only Agentic Conversations + +Use `onlyAgentic = true` to fetch only AI agent conversations. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ..onlyAgentic = true +).build(); +``` + + + + +`hideAgentic` and `onlyAgentic` are mutually exclusive — use only one per request. + + +When conversations are fetched successfully, the response will include only conversations with AI agents. Agent users have `role: "@agentic"` and include agent-specific metadata. + +### Fetch Conversations + +After configuring the builder, call `build()` to create the request, then `fetchNext()` to retrieve conversations. Maximum 50 per request. Call `fetchNext()` repeatedly on the same object to paginate. + + + +```dart +ConversationsRequest conversationRequest = (ConversationsRequestBuilder() + ..limit = 50 + ).build(); + +conversationRequest.fetchNext( + onSuccess: (List conversations){ + + + }, onError: (CometChatException e){ + + }); +``` + + + + +**On Success** — A `List` containing conversation objects with their associated user/group and last message details: + + + +**Conversation Object (per item in list):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `conversationType` | string | Type of conversation | `"user"` | +| `conversationWith` | object | User or Group the conversation is with | [See below ↓](#fetch-conversations-conversationwith-user-object) | +| `lastMessage` | object | Last message in the conversation | [See below ↓](#fetch-conversations-lastmessage-object) | +| `updatedAt` | number | Epoch timestamp of last update | `1745554729` | +| `unreadMessageCount` | number | Count of unread messages | `2` | +| `tags` | array | Tags associated with the conversation | `[]` | +| `unreadMentionsCount` | number | Count of unread mentions | `0` | +| `lastReadMessageId` | number | ID of the last read message | `398` | +| `latestMessageId` | number | ID of the latest message | `401` | + +--- + + + +**`conversationWith` Object (User):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-2"` | +| `name` | string | Display name of the user | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + +--- + + + +**`conversationWith` Object (Group):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `guid` | string | Unique identifier of the group | `"cometchat-guid-1"` | +| `name` | string | Display name of the group | `"Flutter Devs"` | +| `icon` | string | Group icon URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-3.webp"` | +| `description` | string | Group description | `"A group for Flutter developers"` | +| `membersCount` | number | Number of members in the group | `5` | +| `metadata` | object | Custom metadata | `{}` | +| `joinedAt` | number | Epoch timestamp when the user joined | `1745500000` | +| `hasJoined` | boolean | Whether the current user has joined | `true` | +| `createdAt` | number | Epoch timestamp when the group was created | `1745400000` | +| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | +| `updatedAt` | number | Epoch timestamp of last update | `1745554729` | +| `tags` | array | Group tags | `[]` | +| `type` | string | Group type | `"public"` | +| `scope` | string | Scope of the current user in the group | `"admin"` | +| `password` | string | Group password (for password-protected groups) | `null` | +| `isBannedFromGroup` | boolean | Whether the current user is banned | `false` | + +--- + + + +**`lastMessage` Object (TextMessage):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `401` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-conversations-lastmessage-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-conversations-lastmessage-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `1745554730` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `text` | string | The text content of the message | `"Hey, are you available for a call?"` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `moderationStatus` | string | Moderation status of the message | `null` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`lastMessage.sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + +--- + + + +**`lastMessage.receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while retrieving conversations."` | + + + +The `Conversation` object consists of the below fields: + +| Field | Information | +| --------------------- | -------------------------------------------------------------------- | +| `conversationId` | ID of the conversation | +| `conversationType` | Type of conversation (user/group) | +| `lastMessage` | Last message the conversation | +| `conversationWith` | `User` or `Group` object containing the details of the user or group | +| `unreadMessageCount` | Unread message count for the conversation | +| `unreadMentionsCount` | the count of unread mentions in the conversation. | +| `lastReadMessageId` | the ID of the last read message in the conversation. | + +## Tag Conversation + +*In other words, as a logged-in user, how do I tag a conversation?* + +Use `tagConversation()` to add tags to a conversation. + +| Parameter | Description | +| --- | --- | +| `conversationWith` | UID or GUID of the user/group | +| `conversationType` | `user` or `group` | +| `tags` | Array of tags to add | + + + +```dart +String conversationWith = "cometchat-uid-1"; //id of the user/group +String conversationType = "user"; +List tags = []; +tags.add("archived"); + +CometChat.tagConversation(conversationWith, conversationType, tags, + onSuccess: (Conversation conversation) { + debugPrint("Conversation tagged Successfully : $conversation"); + }, onError: (CometChatException e) { + debugPrint("Conversation tagging failed : ${e.message}"); + } +); +``` + + + + +**On Success** — A `Conversation` object containing the updated conversation with the applied tags: + + + +**Conversation Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `conversationType` | string | Type of conversation | `"user"` | +| `conversationWith` | object | User or Group the conversation is with | [See below ↓](#tag-conversation-conversationwith-user-object) | +| `lastMessage` | object | Last message in the conversation | [See below ↓](#tag-conversation-lastmessage-object) | +| `updatedAt` | number | Epoch timestamp of last update | `1745554729` | +| `unreadMessageCount` | number | Count of unread messages | `0` | +| `tags` | array | Tags associated with the conversation | `["archived"]` | +| `unreadMentionsCount` | number | Count of unread mentions | `0` | +| `lastReadMessageId` | number | ID of the last read message | `398` | +| `latestMessageId` | number | ID of the latest message | `401` | + +--- + + + +**`conversationWith` Object (User):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`lastMessage` Object (TextMessage):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `401` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#tag-conversation-lastmessage-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#tag-conversation-lastmessage-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `1745554730` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `text` | string | The text content of the message | `"Hey, are you available for a call?"` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `moderationStatus` | string | Moderation status of the message | `null` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`lastMessage.sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`lastMessage.receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to tag the conversation."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while tagging the conversation."` | + + + + + +The tags for conversations are one-way. This means that if user A tags a conversation with user B, that tag will be applied to that conversation only for user A. + + + +## Retrieve Single Conversation + +*In other words, as a logged-in user, how do I retrieve a specific conversation?* + +Use `getConversation()` to fetch a specific conversation. + +| Parameter | Description | +| --- | --- | +| `conversationWith` | UID or GUID of the user/group | +| `conversationType` | `user` or `group` | + + + +```dart +String conversationWith = "cometchat-uid-1"; //id of the user/group +String conversationType = "user"; +CometChat.getConversation(conversationWith, conversationType, + onSuccess: (Conversation conversation) { + debugPrint("Fetch Conversation Successfully : $conversation"); + }, onError: (CometChatException e) { + debugPrint("Fetch Conversation failed : ${e.message}"); + } +); +``` + + + + +**On Success** — A `Conversation` object containing the details of the requested conversation: + + + +**Conversation Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `conversationType` | string | Type of conversation | `"user"` | +| `conversationWith` | object | User or Group the conversation is with | [See below ↓](#get-conversation-conversationwith-user-object) | +| `lastMessage` | object | Last message in the conversation | [See below ↓](#get-conversation-lastmessage-object) | +| `updatedAt` | number | Epoch timestamp of last update | `1745554729` | +| `unreadMessageCount` | number | Count of unread messages | `0` | +| `tags` | array | Tags associated with the conversation | `[]` | +| `unreadMentionsCount` | number | Count of unread mentions | `0` | +| `lastReadMessageId` | number | ID of the last read message | `398` | +| `latestMessageId` | number | ID of the latest message | `401` | + +--- + + + +**`conversationWith` Object (User):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`lastMessage` Object (TextMessage):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `401` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#get-conversation-lastmessage-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `1745554730` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#get-conversation-lastmessage-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `1745554730` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `text` | string | The text content of the message | `"Hey, are you available for a call?"` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `moderationStatus` | string | Moderation status of the message | `null` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`lastMessage.sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`lastMessage.receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while retrieving the conversation."` | + + + +## Convert Messages to Conversations + +As per our [Receive Messages](/sdk/flutter/receive-messages) guide, for real-time messages, you will always receive `Message` objects and not `Conversation` objects. Thus, you will need a mechanism to convert the `Message` object to a `Conversation` object. You can use the `getConversationFromMessage` method for this purpose. + + + +```dart +Conversation conversation = CometChat.getConversationFromMessage(message); +``` + + + + +While converting a `Message` object to a `Conversation` object, the `unreadMessageCount` & `tags` will not be available in the `Conversation` object. The unread message count needs to be managed in your client-side code. + + +--- + +## Next Steps + + + + Remove conversations from the logged-in user's list + + + Show real-time typing status in conversations + + + Track message delivery and read status + + + Listen for incoming messages in real-time + + diff --git a/sdk/flutter/v4/retrieve-group-members.mdx b/sdk/flutter/v4/retrieve-group-members.mdx new file mode 100644 index 000000000..1eaa8a1ba --- /dev/null +++ b/sdk/flutter/v4/retrieve-group-members.mdx @@ -0,0 +1,222 @@ +--- +title: "Retrieve Group Members" +sidebarTitle: "Retrieve Members" +description: "Fetch and filter group members by scope, status, and search keyword using the CometChat Flutter SDK with pagination support." +--- + + + +```dart +// Retrieve group members with pagination +GroupMembersRequest request = (GroupMembersRequestBuilder("GROUP_ID") + ..limit = 30 +).build(); + +await request.fetchNext( + onSuccess: (List members) { + for (GroupMember member in members) { + debugPrint("Member: ${member.name}, Scope: ${member.scope}"); + } + }, + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Filter by scope (admin, moderator, participant) +GroupMembersRequest scopedRequest = (GroupMembersRequestBuilder("GROUP_ID") + ..limit = 30 + ..scopes = ["admin", "moderator"] +).build(); + +// Search members +GroupMembersRequest searchRequest = (GroupMembersRequestBuilder("GROUP_ID") + ..limit = 30 + ..searchKeyword = "john" +).build(); +``` + + +Fetch the members of a group with filtering by scope, online status, and search keyword. Results are returned as [`GroupMember`](/sdk/reference/entities#groupmember) objects, which extend [`User`](/sdk/reference/entities#user) with group-specific fields like scope. + +## Retrieve the List of Group Members + +In order to fetch the list of groups members for a group, you can use the `GroupMembersRequest` class. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +To use this class i.e to create an object of the `GroupMembersRequest` class, you need to use the `GroupMembersRequestBuilder` class. The `GroupMembersRequestBuilder` class allows you to set the parameters based on which the groups are to be fetched. + +The `GUID` of the group for which the members are to be fetched must be specified in the constructor of the `GroupMembersRequestBuilder` class. + +### GroupMembersRequestBuilder + +| Parameter | Type | Description | +|-----------|------|-------------| +| `guid` | `String` | **(Required, constructor)** Group ID for the group whose members are to be fetched. | +| `limit` | `int?` | Maximum number of members to fetch per request. Max `100`, default `30`. | +| `searchKeyword` | `String?` | Search string to filter members by name. | +| `scopes` | `List?` | Filter members by scope (`"admin"`, `"moderator"`, `"participant"`). | +| `status` | `String?` | Filter members by online status (`"online"`, `"offline"`). If not set, returns all members. | +| `setPage` | `int?` | Fetch group members from a particular page number. | + +### Set Limit + +This method sets the limit i.e. the number of members that should be fetched in a single iteration. + + + +```dart +String GUID = "GUID"; + GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) + ..limit = 20 + ).build(); +``` + + + + + +### Set Search Keyword + +This method allows you to set the search string based on which the group members are to be fetched. + + + +```dart +String GUID = "GUID"; +GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) + ..limit = 20 + ..searchKeyword = "abc" + ).build(); +``` + + + + + +### Set Scopes + +This method allows you to fetch group members based on the specified scopes. + + + +```dart +List scopes =[]; +scopes.add("admin"); +scopes.add("moderator"); +String GUID = "GUID"; +GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) + ..limit = 20 + ..scopes = scopes + ).build(); +``` + + + + + +Relevant fields to access on returned members: + +| Field | Property | Return Type | Description | +|-------|----------|-------------|-------------| +| scope | `scope` | `String` | Scope of the member in the group (`"admin"`, `"moderator"`, or `"participant"`) | + +### Set Status + +Filters members by online status: + +| Value | Description | +|-------|-------------| +| `"online"` | Only online members | +| `"offline"` | Only offline members | + +If not set, returns all members regardless of status. + + + +```dart +String GUID = "GUID"; +GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) + ..limit = 20 + ..status = "online" +).build(); +``` + + + + + +Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `GroupMembersRequest` class. + +Once you have the object of the `GroupMembersRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `GroupMember` objects containing N number of members depending on the limit set. + + + +```dart +String GUID = "GUID"; +GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) + ..limit = 20 + ).build(); + +groupMembersRequest.fetchNext(onSuccess: (List groupMemberList){ + debugPrint("Group Members fetched Successfully : $groupMemberList "); + }, onError: (CometChatException e) { + debugPrint("Delete Group failed with exception: ${e.message}"); + }); +``` + + + + + +The `fetchNext()` method returns a list of [`GroupMember`](/sdk/reference/entities#groupmember) objects. `GroupMember` extends [`User`](/sdk/reference/entities#user) and adds group-specific fields. + + +**On Success** — A `List` containing the group members for the specified group (each item is a `GroupMember` object): + + + +**GroupMember Object (per item in array):** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | +| `scope` | string | Member scope in the group | `"admin"` | +| `joinedAt` | number | Epoch timestamp when the member joined the group | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified group does not exist."` | +| `details` | string | Additional technical details | `"Please verify the group ID and try again."` | + + + +--- + +## Next Steps + + + + Add new members to your groups + + + Remove or ban members from groups + + diff --git a/sdk/flutter/v4/retrieve-groups.mdx b/sdk/flutter/v4/retrieve-groups.mdx new file mode 100644 index 000000000..d5182cea2 --- /dev/null +++ b/sdk/flutter/v4/retrieve-groups.mdx @@ -0,0 +1,346 @@ +--- +title: "Retrieve Groups" +sidebarTitle: "Retrieve Groups" +description: "Fetch, filter, and search groups using the CometChat Flutter SDK. Includes pagination, tag-based filtering, joined-only groups, and online member counts." +--- + + + +```dart +// Retrieve groups with pagination +GroupsRequest request = (GroupsRequestBuilder() + ..limit = 30 + ..searchKeyword = "search_term" +).build(); + +await request.fetchNext( + onSuccess: (List groups) { + for (Group group in groups) { + debugPrint("Group: ${group.name}"); + } + }, + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Get a specific group by GUID +await CometChat.getGroup( + "GROUP_ID", + onSuccess: (Group group) => debugPrint("Group: ${group.name}"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); + +// Fetch only joined groups +GroupsRequest joinedRequest = (GroupsRequestBuilder() + ..limit = 30 + ..joinedOnly = true +).build(); + +// Get online member count +CometChat.getOnlineGroupMemberCount(["GUID"], + onSuccess: (Map count) => debugPrint("Count: $count"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); +``` + + +Retrieve groups allows you to fetch the list of groups you've joined and groups that are available, as well as get details for a specific group. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Retrieve List of Groups + +*In other words, as a logged-in user, how do I retrieve the list of groups I've joined and groups that are available?* + +In order to fetch the list of groups, you can use the `GroupsRequest` class. To use this class i.e to create an object of the `GroupsRequest` class, you need to use the `GroupsRequestBuilder` class. The `GroupsRequestBuilder` class allows you to set the parameters based on which the groups are to be fetched. + +Use `GroupsRequestBuilder` to fetch groups with filtering, searching, and pagination. + +### GroupsRequestBuilder + +| Parameter | Type | Description | +|-----------|------|-------------| +| `limit` | `int?` | Maximum number of groups to fetch per request. Max `100`, default `30`. | +| `searchKeyword` | `String?` | Search string to filter groups by name. | +| `joinedOnly` | `bool?` | When `true`, returns only groups the logged-in user has joined. Default `false`. | +| `tags` | `List?` | List of tags to filter groups by. Only groups with the specified tags are returned. | +| `withTags` | `bool?` | When `true`, includes tag data in the returned group objects. Default `false`. | +| `setPage` | `int?` | Fetch groups from a particular page number. | + +### Set Limit + +Sets the number of groups to fetch per request. + + + +```dart +GroupsRequest groupsRequest = (GroupsRequestBuilder() + ..limit= 20 +).build(); +``` + + + + + +### Set Search Keyword + +Filters groups by a search string. + + + +```dart +GroupsRequest groupsRequest = (GroupsRequestBuilder() + ..limit= 20 + ..searchKeyword = "abc" +).build(); +``` + + + + + +### Joined Only + +When `true`, returns only groups the logged-in user has joined or is a part of. + + + +```dart +GroupsRequest groupsRequest = (GroupsRequestBuilder() + ..limit= 20 + ..joinedOnly = true +).build(); +``` + + + + + +### Set Tags + +Filters groups by specified tags. The list fetched will only contain the groups that have been tagged with the specified tags. + + + +```dart +List tags =[]; +tags.add("archived"); +GroupsRequest groupsRequest = (GroupsRequestBuilder() + ..limit= 20 + ..tags = tags +).build(); +``` + + + + + +### With Tags + +When `true`, includes tag data in the returned group objects. + + + +```dart +GroupsRequest groupsRequest = (GroupsRequestBuilder() + ..limit= 20 + ..withTags = true +).build(); +``` + + + + + +Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `GroupsRequest` class. + +Once you have the object of the `GroupsRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of [`Group`](/sdk/reference/entities#group) objects containing 'n' number of groups depending on the limit set. + +The list of groups fetched will only have the public and password type groups. The private groups will only be available if the user is a member of that private group. + + + +```dart +GroupsRequest groupsRequest = (GroupsRequestBuilder() + ..limit= 20 +).build(); + +groupsRequest.fetchNext(onSuccess: (List groupList) { + debugPrint("Fetched Group Successfully : $groupList "); + }, onError: (CometChatException e) { + debugPrint("Group Request failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched groups. Each `Group` object has the following structure: + + + +**Group Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | +| `name` | string | Display name of the group | `"Tech Enthusiasts"` | +| `icon` | string | URL of the group icon | `null` | +| `description` | string | Description of the group | `"A group for tech lovers"` | +| `membersCount` | number | Number of members in the group | `12` | +| `metadata` | object | Custom metadata attached to the group | `{}` | +| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | +| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | +| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | +| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | +| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | +| `tags` | array | List of tags associated with the group | `[]` | +| `type` | string | Type of the group (public, private, password) | `"public"` | +| `scope` | string | Scope of the logged-in user in the group | `"admin"` | +| `password` | string | Password for password-protected groups | `null` | +| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Retrieve Particular Group Details + +*In other words, as a logged-in user, how do I retrieve information for a specific group?* + +To get the information of a group, you can use the `getGroup()` method. + + + +```dart +String GUID = "GUID"; + +CometChat.getGroup(GUID, onSuccess: (Group group) { + debugPrint("Fetched Group Successfully : $group "); + }, onError: (CometChatException e) { + debugPrint("Group Request failed with exception: ${e.message}"); + }); +``` + + + + + +| Parameter | Description | +| --------- | ------------------------------------------------------------ | +| `GUID` | The GUID of the group for whom the details are to be fetched | + +On success, the [`Group`](/sdk/reference/entities#group) object containing the details of the group is returned. + + +**On Success** — A `Group` object containing all details of the requested group: + + + +**Group Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | +| `name` | string | Display name of the group | `"Tech Enthusiasts"` | +| `icon` | string | URL of the group icon | `null` | +| `description` | string | Description of the group | `"A group for tech lovers"` | +| `membersCount` | number | Number of members in the group | `12` | +| `metadata` | object | Custom metadata attached to the group | `{}` | +| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | +| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | +| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | +| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | +| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554729` | +| `tags` | array | List of tags associated with the group | `[]` | +| `type` | string | Type of the group (public, private, password) | `"public"` | +| `scope` | string | Scope of the logged-in user in the group | `"admin"` | +| `password` | string | Password for password-protected groups | `null` | +| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified group does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | + + + +## Get online group member count + +To get the total count of online users in particular groups, you can use the `getOnlineGroupMemberCount()` method. + + + +```dart +List guids = []; +guids.add("cometchat-guid-1"); +guids.add("cometchat-guid-11"); + +CometChat.getOnlineGroupMemberCount(guids, + onSuccess: (Map count) { + debugPrint("Fetched Online Group Member Count Successfully : $count "); + }, onError: (CometChatException e) { + debugPrint("Online Group Member failed with exception: ${e.message}"); + }); +``` + + + + + +This method returns a `Map` with the GUID of the group as the key and the online member count for that group as the value. + + +**On Success** — A `Map` containing the GUID of each group as the key and the online member count as the value: + + + +**Map Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `cometchat-guid-1` | number | Online member count for the group | `3` | +| `cometchat-guid-11` | number | Online member count for the group | `7` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + + +--- + +## Next Steps + + + + Create new public, private, or password-protected groups + + + Get the list of members in a group + + diff --git a/sdk/flutter/v4/retrieve-users.mdx b/sdk/flutter/v4/retrieve-users.mdx new file mode 100644 index 000000000..1331d8932 --- /dev/null +++ b/sdk/flutter/v4/retrieve-users.mdx @@ -0,0 +1,453 @@ +--- +title: "Retrieve Users" +sidebarTitle: "Retrieve Users" +description: "Fetch, filter, search, and sort users using the CometChat Flutter SDK. Includes pagination, role-based filtering, tag support, and online user counts." +--- + + + +```dart +// Fetch users list +UsersRequest usersRequest = (UsersRequestBuilder()..limit = 30).build(); +usersRequest.fetchNext( + onSuccess: (List userList) => debugPrint("Users: $userList"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}") +); + +// Get specific user details +CometChat.getUser("UID", + onSuccess: (User user) => debugPrint("User: $user"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}") +); + +// Get logged-in user +User? user = await CometChat.getLoggedInUser(); + +// Get online user count +CometChat.getOnlineUserCount( + onSuccess: (int count) => debugPrint("Online: $count"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}") +); +``` + + +The CometChat SDK provides methods to retrieve the logged-in user, fetch filtered user lists, look up individual users by UID, and get online user counts. All user methods return [`User`](/sdk/reference/entities#user) objects. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +### User Object Fields + +| Field | Type | Description | +|-------|------|-------------| +| `uid` | `String` | Unique user ID | +| `name` | `String` | Display name of the user | +| `avatar` | `String?` | URL of the user's avatar image | +| `status` | `String` | Online status of the user (`"online"` or `"offline"`) | +| `lastActiveAt` | `int?` | Epoch timestamp when the user was last active | +| `role` | `String` | Role assigned to the user | +| `tags` | `List` | Tags associated with the user | + +## Retrieve Logged In User Details + +Use `getLoggedInUser()` to get the current user's details. Returns `null` if no user is logged in. + + + +```dart +User? user = await CometChat.getLoggedInUser(); +``` + + + +This method returns a [`User`](/sdk/reference/entities#user) object with the logged-in user's information. + +## Retrieve List of Users + +In order to fetch the list of users, you can use the `UsersRequest` class. To use this class i.e to create an object of the `UsersRequest` class, you need to use the `UsersRequestBuilder` class. The `UsersRequestBuilder` class allows you to set the parameters based on which the users are to be fetched. + +Fetching using this builder will return [`User`](/sdk/reference/entities#user) objects. + +### UsersRequestBuilder Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `limit` | `int?` | Number of users to fetch per request | +| `searchKeyword` | `String?` | Filters users by a search string | +| `searchIn` | `List?` | Specifies which user properties to search (`"uid"`, `"name"`). Works with `searchKeyword`. | +| `userStatus` | `String?` | Filters by online status (`CometChatUserStatus.online` or `CometChatUserStatus.offline`) | +| `hideBlockedUsers` | `bool?` | When `true`, excludes users blocked by the logged-in user | +| `roles` | `List?` | Filters users by specified roles | +| `friendsOnly` | `bool?` | When `true`, returns only friends of the logged-in user | +| `tags` | `List?` | Filters users by specified tags | +| `withTags` | `bool?` | When `true`, includes tag data in the returned user objects | +| `uids` | `List?` | Fetches specific users by their UIDs. Maximum 25 per request. | +| `sortBy` | `String?` | Sorts the user list by a specific property. Default sort order: `status → name → UID`. Pass `"name"` to sort by `name → UID`. | +| `sortByOrder` | `String?` | Sets the sort order. Default is ascending (`"asc"`). Use `"desc"` for descending. | + +The `UsersRequestBuilder` class allows you to set the below parameters: + +### Set Limit + +Sets the number of users to fetch per request. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ).build(); +``` + + + +### Set Search Keyword + +Filters users by a search string. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..searchKeyword = "abc" + ).build(); +``` + + + +### Search In + +Specifies which user properties to search. Works with `searchKeyword`. By default, searches both UID and name. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..searchKeyword = "super" + ..searchIn = ["uid", "name"] + ).build(); +``` + + + +### Set Status + +Filters users by online status: + +- `CometChatUserStatus.online` — Only online users +- `CometChatUserStatus.offline` — Only offline users + +If not set, returns all users. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..userStatus = CometChatUserStatus.online + ).build(); +``` + + + +### Hide Blocked Users + +When `true`, excludes users blocked by the logged-in user from the results. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..hideBlockedUsers = true + ).build(); +``` + + + +### Set Roles + +Filters users by specified roles. + + + +```dart +List roles = []; +roles.add("role1"); +roles.add("role2"); +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..roles = roles + ).build(); +``` + + + +### Friends Only + +When `true`, returns only friends of the logged-in user. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..friendsOnly = true + ).build(); +``` + + + +### Set Tags + +Filters users by specified tags. + + + +```dart +List tags = []; +tags.add("tag1"); +tags.add("tag2"); +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..tags = tags + ).build(); +``` + + + +### With Tags + +When `true`, includes tag data in the returned user objects. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 50 + ..withTags = true + ).build(); +``` + + + +### Set UIDs + +Fetches specific users by their UIDs. Maximum 25 users per request. + + + +```dart +List uids = []; +uids.add("UID1"); +uids.add("UID2"); +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 25 + ..uids = uids + ).build(); +``` + + + +### Sort By + +Sorts the user list by a specific property. Default sort order: `status → name → UID`. Pass `"name"` to sort by `name → UID`. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 30 + ..sortBy = "name" + ).build(); +``` + + + +### Sort By Order + +Sets the sort order. Default is ascending (`"asc"`). Use `"desc"` for descending. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 30 + ..sortByOrder = "desc" + ).build(); +``` + + + +After configuring the builder, call `build()` to get the `UsersRequest` object, then call `fetchNext()` to retrieve users. + + + +```dart +UsersRequest usersRequest = (UsersRequestBuilder() + ..limit = 25 + ).build(); + +usersRequest.fetchNext(onSuccess: (List userList){ + debugPrint("User List Fetched Successfully : $userList"); + },onError: (CometChatException e){ + debugPrint("User List Fetch Failed: ${e.message}"); + }); +``` + + + +The `fetchNext()` method returns a list of [`User`](/sdk/reference/entities#user) objects. + + +**On Success** — A list of `User` objects matching the request filters. Each item in the list contains: + + + +**User Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Retrieve Particular User Details + +Use `getUser()` to fetch a specific user's details by UID. + + + +```dart +String UID = "UID"; + +CometChat.getUser(UID, onSuccess: (User user){ + debugPrint("User Fetched Successfully : $user"); + }, onError: (CometChatException e){ + debugPrint("User Fetch Failed: ${e.message}"); + }); +``` + + + +The `getUser()` method takes the following parameters: + +| Parameter | Description | +| --------- | ---------------------------------------------------------- | +| `UID` | The UID of the user for whom the details are to be fetched | + +On success, the `User` object containing the details of the user is returned. + + +**On Success** — A `User` object containing the details of the requested user: + + + +**User Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified UID does not exist."` | +| `details` | string | Additional technical details | `"Please verify the UID and try again."` | + + + +## Get Online User Count + +Use `getOnlineUserCount()` to get the total number of online users in your app. + + + +```dart +CometChat.getOnlineUserCount(onSuccess: (int count){ + debugPrint("Online User Count: $count"); + }, onError: (CometChatException e){ + debugPrint("User Count Fetch Failed: ${e.message}"); + }); +``` + + + +`getOnlineUserCount()` resolves with an `int` representing the total count of currently online users in your app. + + +**On Success** — An `int` value representing the total count of online users: + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `count` | number | Total number of online users | `12` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +--- + +## Next Steps + + + + Monitor and manage real-time user online/offline status + + + Block and unblock users to control interactions + + + Create, update, and delete users programmatically + + + Explore all user-related features and capabilities + + diff --git a/sdk/flutter/v4/send-message.mdx b/sdk/flutter/v4/send-message.mdx new file mode 100644 index 000000000..57cc58dfa --- /dev/null +++ b/sdk/flutter/v4/send-message.mdx @@ -0,0 +1,1264 @@ +--- +title: "Send Messages" +sidebarTitle: "Send Messages" +description: "Send text, media, and custom messages to users and groups using the CometChat Flutter SDK." +--- + + + +| Field | Value | +| --- | --- | +| Key Classes | [`TextMessage`](/sdk/reference/messages#textmessage), [`MediaMessage`](/sdk/reference/messages#mediamessage), [`CustomMessage`](/sdk/reference/messages#custommessage) | +| Key Methods | `CometChat.sendMessage()`, `CometChat.sendMediaMessage()`, `CometChat.sendCustomMessage()` | +| Receiver Types | `CometChatReceiverType.user`, `CometChatReceiverType.group` | +| Message Types | `CometChatMessageType.text`, `CometChatMessageType.image`, `CometChatMessageType.video`, `CometChatMessageType.audio`, `CometChatMessageType.file`, `CometChatMessageType.custom` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Send text message to user +TextMessage textMessage = TextMessage( + text: "Hello!", + receiverUid: "UID", + receiverType: CometChatReceiverType.user, + type: CometChatMessageType.text, +); +CometChat.sendMessage(textMessage, onSuccess: (msg) {}, onError: (e) {}); + +// Send text message to group +TextMessage groupMessage = TextMessage( + text: "Hello group!", + receiverUid: "GUID", + receiverType: CometChatReceiverType.group, + type: CometChatMessageType.text, +); +CometChat.sendMessage(groupMessage, onSuccess: (msg) {}, onError: (e) {}); + +// Send media message (image) +MediaMessage mediaMessage = MediaMessage( + receiverUid: "UID", + receiverType: CometChatReceiverType.user, + type: CometChatMessageType.image, + file: "path/to/image.jpg", +); +CometChat.sendMediaMessage(mediaMessage, onSuccess: (msg) {}, onError: (e) {}); + +// Send custom message +CustomMessage customMessage = CustomMessage( + receiverUid: "UID", + receiverType: CometChatReceiverType.user, + type: "location", + customData: {"latitude": "50.6192", "longitude": "-72.6818"}, +); +CometChat.sendCustomMessage(customMessage, onSuccess: (msg) {}, onError: (e) {}); +``` + + + +CometChat supports three types of messages: + +| Type | Method | Use Case | +| --- | --- | --- | +| [Text](#text-message) | `CometChat.sendMessage()` | Plain text messages | +| [Media](#media-message) | `CometChat.sendMediaMessage()` | Images, videos, audio, files | +| [Custom](#custom-message) | `CometChat.sendCustomMessage()` | Location, polls, or any structured data | + +You can also send [Interactive Messages](/sdk/flutter/interactive-messages) for forms, cards, and custom UI elements. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +You can also send metadata along with a text or media message. Think, for example, if you'd want to share the user's location with every message, you can use the metadata field. + +## Text Message + +*In other words, as a sender, how do I send a text message?* + +To send a text message to a single user or group, you need to use the `sendMessage()` method and pass a [`TextMessage`](/sdk/reference/messages#textmessage) object to it. + +### Add Metadata + +To send custom data along with a text message, you can use the `setMetadata` method and pass a `Map` to it. + + + +```dart +Map metadata = {}; +metadata["lattitude"] = "50.6192171633316" ; +metadata["longitude"] = "-72.68182268750002" ; +textMessage.metadata = metadata; +``` + + + + + +### Add Tags + +To add a tag to a message you can assign value in `.tags` variable of the TextMessage Class. `tags` accepts a list of tags. + + + +```dart +List tags = []; +tags.add("pinned"); +textMessage.tags = tags; +``` + + + + + +Once the text message object is ready, you need to use the `sendMessage()` method to send the text message to the recipient. + + + +```dart +String receiverID = "cometchat-uid-1"; +String messageText = "messageText"; +String receiverType = CometChatConversationType.user; +String type = CometChatMessageType.text; + +TextMessage textMessage = TextMessage(text: messageText, + receiverUid: receiverID, + receiverType: receiverType, + type: type); +CometChat.sendMessage(textMessage, + onSuccess: (TextMessage message) { + debugPrint("Message sent successfully: $message"); + }, onError: (CometChatException e) { + debugPrint("Message sending failed with exception: ${e.message}"); + } +); +``` + + + + +```dart +String receiverID = "cometchat-guid-1"; +String messageText = "messageText"; +String receiverType = CometChatConversationType.group; +String type = CometChatMessageType.text; + +TextMessage textMessage = TextMessage(text: messageText, + receiverUid: receiverID, + receiverType: receiverType, + type: type); +CometChat.sendMessage(textMessage, onSuccess: (TextMessage message) { +debugPrint("Message sent successfully: $message"); +}, onError: (CometChatException e) { +debugPrint("Message sending failed with exception: ${e.message}"); +}); +``` + + + + + +The `TextMessage` class constructor takes the following parameters: + +| Parameter | Description | | +| -------------- | -------------------------------------------------------------------------------------------------------------- | -------- | +| `receiverID` | `UID` of the user or `GUID` of the group receiving the message | Required | +| `messageText` | The text message | Required | +| `receiverType` | The type of the receiver- `CometChatReceiverType.user` (user) or `CometChatReceiverType.group` (group) | Required | +| type | The type of the message that needs to be sent which in this case can be: `CometChatMessageType.text`\_\_(text) | | + +When a text message is sent successfully, the response will include a `TextMessage` object which includes all information related to the sent message. + + +**On Success** — A `TextMessage` object containing all details of the sent message: + + + +**TextMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `401` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#send-text-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#send-text-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `text` | string | The text content of the message | `"messageText"` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `moderationStatus` | string | Moderation status of the message | `null` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_EMPTY_MESSAGE_TEXT"` | +| `message` | string | Human-readable error message | `"The text message body is empty."` | +| `details` | string | Additional technical details | `"Please provide a non-empty text for the message."` | + + + +### Set Quoted Message + +To set a quoted message for a message, use the `setQuotedMessageId` and `setQuotedMessage` method of the TextMessage class. This method accepts the ID of the message to be quoted. + + + + +```dart +textMessage.quotedMessageId = 0 +textMessage.quotedMessage = // Pass base message object here that you want to quote. +``` + + + + +Once the text message object is ready, you need to use the `sendMessage()` method to send the text message to the recipient. + + + +```dart +String receiverID = "UID"; +String messageText = "Hello CometChat!"; +String receiverType = CometChatReceiverType.user; +String type = CometChatMessageType.text; + +TextMessage textMessage = TextMessage( + text: messageText, + receiverUid: receiverID, + receiverType: receiverType, + type: type, +); +textMessage.quotedMessageId = 10; + +CometChat.sendMessage(textMessage, + onSuccess: (TextMessage message) { + debugPrint("Message sent successfully: ${message.toString()}"); + }, + onError: (CometChatException e) { + debugPrint("Message sending failed with exception: ${e.message}"); + }, +); +``` + + + +```dart +String receiverID = "GUID"; +String messageText = "Hello CometChat!"; +String receiverType = CometChatReceiverType.group; +String type = CometChatMessageType.text; + +TextMessage textMessage = TextMessage( + text: messageText, + receiverUid: receiverID, + receiverType: receiverType, + type: type, +); +textMessage.quotedMessageId = 10; + +CometChat.sendMessage(textMessage, + onSuccess: (TextMessage message) { + debugPrint("Message sent successfully: ${message.toString()}"); + }, + onError: (CometChatException e) { + debugPrint("Message sending failed with exception: ${e.message}"); + }, +); +``` + + + + + +The `TextMessage` class constructor takes the following parameters: + +| Parameter | Description | | +| -------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------- | +| `receiverID` | `UID` of the user or `GUID` of the group receiving the message | Required | +| `messageText` | The text message | Required | +| `receiverType` | The type of the receiver- `CometChatReceiverType.user` (user) or `CometChatReceiverType.group` (group) | Required | + +When a text message is sent successfully, the response will include a `TextMessage` object which includes all information related to the sent message. + + +**On Success** — A `TextMessage` object containing all details of the sent quoted message: + + + +**TextMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `402` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#send-quoted-text-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554800` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#send-quoted-text-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `-1` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554800` | +| `text` | string | The text content of the message | `"Hello CometChat!"` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `moderationStatus` | string | Moderation status of the message | `null` | +| `quotedMessageId` | number | ID of the quoted message | `401` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_EMPTY_MESSAGE_TEXT"` | +| `message` | string | Human-readable error message | `"The text message body is empty."` | +| `details` | string | Additional technical details | `"Please provide a non-empty text for the message."` | + + + +## Media Message + +*In other words, as a sender, how do I send a media message like photos, videos & files?* + +To send a media message to any user or group, you need to use the `sendMediaMessage()` method and pass a [`MediaMessage`](/sdk/reference/messages#mediamessage) object to it. + +### Add Metadata + +To send custom data along with a media message, you can use the `setMetadata` method and pass a `Map` to it. + + + +```dart +Map metadata = {}; +metadata["lattitude"] = "50.6192171633316" ; +metadata["longitude"] = "-72.68182268750002" ; +mediaMessage.metadata = metadata; +``` + + + + + +### Add Caption(Text along with Media Message) + + + +```dart +mediaMessage.caption = "Message Caption"; +``` + + + + + +### Add Tags + +To add a tag to a message you can use the `setTags()` method of the MediaMessage Class. The `setTags()` method accepts a list of tags. + + + +```dart +List tags = []; +tags.add("pinned"); +mediaMessage.tags = tags; +``` + + + + + +### Set Quoted Message + +To quote a message in a media message, use the `quotedMessageId` property of the MediaMessage class. + + + +```dart +mediaMessage.quotedMessageId = 10; +``` + + + + + +There are 2 ways you can send Media Messages using the CometChat SDK: + +1. **By providing the File :** You can directly share the file object while creating an object of the MediaMessage class. When the media message is sent using the sendMediaMessage() method, this file is then uploaded to CometChat servers and the URL of the file is sent in the success response of the sendMediaMessage() function. + + + +```dart +String receiverID = "cometchat-uid-1"; +String messageType = CometChatMessageType.image; +String receiverType = CometChatConversationType.user; +String filePath = "storage/emulated/0/Download/46.jpg"; +MediaMessage mediaMessage = MediaMessage( + receiverType: receiverType, + type: messageType, + receiverUid: receiverID, + file: filePath, +); + +await CometChat.sendMediaMessage(mediaMessage, + onSuccess: (MediaMessage message) { + debugPrint("Media message sent successfully: ${mediaMessage.metadata}"); + }, onError: (e) { + debugPrint("Media message sending failed with exception: ${e.message}"); + } +); +``` + + + + +```dart +String receiverID = "cometchat-guid-1"; +String messageType = CometChatMessageType.image; +String receiverType = CometChatConversationType.group; +String filePath = "storage/emulated/0/Download/46.jpg"; +MediaMessage mediaMessage = MediaMessage( + receiverType: receiverType, + type: messageType, + receiverUid: receiverID, + file: filePath, +); + +await CometChat.sendMediaMessage(mediaMessage, + onSuccess: (MediaMessage message) { + debugPrint("Media message sent successfully: ${mediaMessage.metadata}"); + }, onError: (e) { + debugPrint("Media message sending failed with exception: ${e.message}"); + } +); +``` + + + + + +The `MediaMessage` class constructor takes the following parameters: + +| Parameter | Description | | +| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | +| receiverId | The UID or GUID of the recipient | Required | +| file | The file object to be sent | Required | +| messageType | The type of the message that needs to be sent which, in this case, can be:
1. `CometChatMessageType.image` (image)
2. `CometChatMessageType.video` (video)
3. `CometChatMessageType.audio` (audio)
4. `CometChatMessageType.file` (file) | Required | +| receiverType | The type of the receiver to whom the message is to be sent, i.e., `CometChatReceiverType.user` (user) or `CometChatReceiverType.group` (group) | Required | + + +**On Success** — A `MediaMessage` object containing all details of the sent media message: + + + +**MediaMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `403` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#send-media-file-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554900` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"image"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#send-media-file-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554900` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `caption` | string | Caption text for the media message | `null` | +| `attachment` | object | File attachment details | [See below ↓](#send-media-file-attachment-object) | +| `file` | string | Local file path | `"storage/emulated/0/Download/46.jpg"` | +| `files` | array | List of additional file paths | `null` | +| `attachments` | array | List of additional attachments | `null` | +| `moderationStatus` | string | Moderation status of the message | `null` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + +--- + + + +**`attachment` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `fileUrl` | string | URL of the uploaded file | `"https://data-us.cometchat.io/assets/images/46.jpg"` | +| `fileName` | string | Name of the file | `"46.jpg"` | +| `fileExtension` | string | File extension | `"jpg"` | +| `fileMimeType` | string | MIME type of the file | `"image/jpeg"` | +| `fileSize` | number | File size in bytes | `24576` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_FILE_TOO_LARGE"` | +| `message` | string | Human-readable error message | `"The file size exceeds the allowed limit."` | +| `details` | string | Additional technical details | `"Maximum allowed file size is 25 MB."` | + + + +2. **By providing the URL of the File:** The second way to send media messages using the CometChat SDK is to provide the SDK with the URL of any file that is hosted on your servers or any cloud storage. To achieve this you will have to make use of the [`Attachment`](/sdk/reference/auxiliary#attachment) class that is available in the MediaMessage class. For more information, you can refer to the below code snippet: + + + +```dart +String receiverID = "cometchat-uid-1"; +String messageType = CometChatMessageType.image; +String receiverType = CometChatConversationType.user; + +MediaMessage mediaMessage = MediaMessage( + receiverType: receiverType, + type: messageType, + receiverUid: receiverID, + file: null); + +String fileUrl = "https://pngimg.com/uploads/mario/mario_PNG125.png"; +String fileName = "test"; +String fileExtension = "png"; +String fileMimeType = "image/png"; + +Attachment attach = Attachment(fileUrl, fileName, fileExtension, fileMimeType, null); +mediaMessage.attachment = attach; + +await CometChat.sendMediaMessage(mediaMessage, + onSuccess: (MediaMessage message) { + debugPrint("Media message sent successfully: ${mediaMessage}"); + }, onError: (CometChatException e) { + debugPrint("Media message sending failed with exception: ${e.message}"); + } +); +``` + + + + +```dart +String receiverID = "cometchat-guid-1"; +String messageType = CometChatMessageType.image; +String receiverType = CometChatConversationType.group; + +MediaMessage mediaMessage = MediaMessage( + receiverType: receiverType, + type: messageType, + receiverUid: receiverID, + file: null); + +String fileUrl = "https://pngimg.com/uploads/mario/mario_PNG125.png"; +String fileName = "test"; +String fileExtension = "png"; +String fileMimeType = "image/png"; + +Attachment attach = Attachment(fileUrl, fileName, fileExtension, fileMimeType, null); +mediaMessage.attachment = attach; + +await CometChat.sendMediaMessage(mediaMessage, + onSuccess: (MediaMessage message) { + debugPrint("Media message sent successfully: ${mediaMessage}"); + }, onError: (CometChatException e) { + debugPrint("Media message sending failed with exception: ${e.message}"); + } +); +``` + + + + + +When a media message is sent successfully, the response will include a `MediaMessage` object which includes all information related to the sent message. + + +**On Success** — A `MediaMessage` object containing all details of the sent media message (via URL): + + + +**MediaMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `404` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#send-media-url-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745555000` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"image"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#send-media-url-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745555000` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `caption` | string | Caption text for the media message | `null` | +| `attachment` | object | File attachment details | [See below ↓](#send-media-url-attachment-object) | +| `file` | string | Local file path | `null` | +| `files` | array | List of additional file paths | `null` | +| `attachments` | array | List of additional attachments | `null` | +| `moderationStatus` | string | Moderation status of the message | `null` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + +--- + + + +**`attachment` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `fileUrl` | string | URL of the file | `"https://pngimg.com/uploads/mario/mario_PNG125.png"` | +| `fileName` | string | Name of the file | `"test"` | +| `fileExtension` | string | File extension | `"png"` | +| `fileMimeType` | string | MIME type of the file | `"image/png"` | +| `fileSize` | number | File size in bytes | `null` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_INVALID_MESSAGE_TYPE"` | +| `message` | string | Human-readable error message | `"The message type provided is not supported."` | +| `details` | string | Additional technical details | `"Supported types are image, video, audio, and file."` | + + + +If you wish to send a caption or some text along with the Media Message, you can use the `caption field` provided by the MediaMessage class. To get and set the caption you can use the `.caption` variable . As with text messages, the metadata field can be used with media messages as well. Any additional information can be passed along with the media message as a `Map`. + +## Custom Message + +*In other words, as a sender, how do I send a custom message like location co-ordinates?* + +CometChat allows you to send custom messages which are neither text nor media messages. + +In order to send a custom message, you need to use the `sendCustomMessage()` method. + +The `sendCustomMessage()` methods takes an object of the [`CustomMessage`](/sdk/reference/messages#custommessage) which can be obtained using the below constructor. + + + +```dart +CustomMessage customMessage = CustomMessage( receiverUid: UID, + type: type, + customData: customData, + receiverType: receiverType, + subType: subType, + ); +``` + + + + + +The above constructor, helps you create a custom message with the message type set to whatever is passed to the constructor and the category set to `custom`. + +The `CustomMessage` class constructor takes the following parameters: + +| Parameter | Description | Required | +| --- | --- | --- | +| `receiverUid` | UID of the user or GUID of the group to which the message is to be sent | Yes | +| `receiverType` | Type of the receiver — `CometChatConversationType.user` or `CometChatConversationType.group` | Yes | +| `type` | Custom message type string (e.g., `"location"`, `"poll"`) | Yes | +| `customData` | The data to be passed as the message in the form of a `Map` | Yes | +| `subType` | Optional sub-type for the custom message | No | + +You can also use the subType field of the `CustomMessage` class to set a specific type for the custom message. This can be achieved using the `subtype` field. + +### Add Metadata + +To send custom data along with a custom message, you can use the `metadata` property and pass a `Map` to it. + + + +```dart +Map metadata = {}; +metadata["priority"] = "high"; +metadata["source"] = "mobile"; +customMessage.metadata = metadata; +``` + + + + + +### Add Tags + +To add a tag to a message you can assign value in `.tags` variable of the CustomMessage Class. `tags` accepts a list of tags. + + + +```dart +List tags = []; +tags.add("pinned"); +textMessage.tags = tags; +``` + + + + + +### Set Quoted Message + +To quote a message in a custom message, use the `quotedMessageId` property of the CustomMessage class. + + + +```dart +customMessage.quotedMessageId = 10; +``` + + + + + +Once the object of `CustomMessage` class is ready you can send the custom message using the `sendCustomMessage()` method. + + + +```dart +String UID = "UID"; +String subType = "LOCATION"; +String receiverType = CometChatConversationType.user; +String type = CometChatMessageType.custom; + +Map customData = {}; +customData["latitude"] = "19.0760"; +customData["longitude"] = "72.8777"; + +CustomMessage customMessage = CustomMessage( + receiverUid: UID, + type: type, + customData: customData, + receiverType: receiverType, + subType: subType, +); + +CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { + debugPrint("Custom Message Sent Successfully : $message"); +}, onError: (CometChatException e) { + debugPrint("Custom message sending failed with exception: ${e.message}"); +}); +``` + + + + +```dart +String GUID = "GUID"; +String subType = "LOCATION"; +String receiverType = CometChatConversationType.group; +String type = CometChatMessageType.custom; + +Map customData = {}; +customData["latitude"] = "19.0760"; +customData["longitude"] = "72.8777"; + +CustomMessage customMessage = CustomMessage( + receiverUid: GUID, + type: type, + customData: customData, + receiverType: receiverType, + subType: subType, +); + +CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { + debugPrint("Custom Message Sent Successfully : $message"); +}, onError: (CometChatException e) { + debugPrint("Custom message sending failed with exception: ${e.message}"); +}); +``` + + + + + +The above sample explains how custom messages can be used to share the location with a user. Similarly, you can send custom messages to groups. + +On success, you will receive an object of `CustomMessage` class. + + +**On Success** — A `CustomMessage` object containing all details of the sent custom message: + + + +**CustomMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `405` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#send-custom-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745555100` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"custom"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#send-custom-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"custom"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745555100` | +| `customData` | object | Custom data payload | `{"latitude": "19.0760", "longitude": "72.8777"}` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `text` | string | Conversation text for notifications | `null` | +| `updateConversation` | boolean | Whether to update the conversation's last message | `false` | +| `sendNotification` | boolean | Whether to send a push notification | `false` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to send the custom message."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +### Update Conversation + +*How can I decide whether the custom message should update the last message of a conversation?* + +By default, a custom message will update the last message of a conversation. If you wish to not update the last message of the conversation when a custom message is sent, please use `updateConversation` (boolean value) method of the Custom Message. + + + +```dart +String UID = "UID"; +String subType = "LOCATION"; +String receiverType = CometChatConversationType.user; +String type = CometChatMessageType.custom; + +Map customData = {}; +customData["latitude"] = "19.0760"; +customData["longitude"] = "72.8777"; + +CustomMessage customMessage = CustomMessage( + receiverUid: UID, + type: type, + customData: customData, + receiverType: receiverType, + subType: subType, +); +customMessage.updateConversation = false; + +CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { + debugPrint("Custom Message Sent Successfully : $message"); +}, onError: (CometChatException e) { + debugPrint("Custom message sending failed with exception: ${e.message}"); +}); +``` + + + + +```dart +String GUID = "GUID"; +String subType = "LOCATION"; +String receiverType = CometChatConversationType.group; +String type = CometChatMessageType.custom; + +Map customData = {}; +customData["latitude"] = "19.0760"; +customData["longitude"] = "72.8777"; + +CustomMessage customMessage = CustomMessage( + receiverUid: GUID, + type: type, + customData: customData, + receiverType: receiverType, + subType: subType, +); +customMessage.updateConversation = false; + +CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { + debugPrint("Custom Message Sent Successfully : $message"); +}, onError: (CometChatException e) { + debugPrint("Custom message sending failed with exception: ${e.message}"); +}); +``` + + + + + +### Custom Notification Body + +*How can i customise the notification body of custom message?* + +To add a custom notification body for `Push, Email & SMS` notification of a custom message you can use the `conversationText` method of Custom Message class. + + + +```dart +String UID = "UID"; +String subType = "LOCATION"; +String receiverType = CometChatConversationType.user; +String type = CometChatMessageType.custom; + +Map customData = {}; +customData["latitude"] = "19.0760"; +customData["longitude"] = "72.8777"; + +CustomMessage customMessage = CustomMessage( + receiverUid: UID, + type: type, + customData: customData, + receiverType: receiverType, + subType: subType, +); +customMessage.conversationText = "Custom Notification Body"; + +CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { + debugPrint("Custom Message Sent Successfully : $message"); +}, onError: (CometChatException e) { + debugPrint("Custom message sending failed with exception: ${e.message}"); +}); +``` + + + + +```dart +String GUID = "GUID"; +String subType = "LOCATION"; +String receiverType = CometChatConversationType.group; +String type = CometChatMessageType.custom; + +Map customData = {}; +customData["latitude"] = "19.0760"; +customData["longitude"] = "72.8777"; + +CustomMessage customMessage = CustomMessage( + receiverUid: GUID, + type: type, + customData: customData, + receiverType: receiverType, + subType: subType, +); +customMessage.conversationText = "Custom Notification Body"; + +CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { + debugPrint("Custom Message Sent Successfully : $message"); +}, onError: (CometChatException e) { + debugPrint("Custom message sending failed with exception: ${e.message}"); +}); +``` + + + + + + + +It is also possible to send interactive messages from CometChat , to know more [click here](/sdk/flutter/interactive-messages) + + + +--- + +## Next Steps + + + + Handle incoming messages in real-time + + + Modify sent messages after delivery + + + Send forms, cards, and interactive elements + + + Remove messages from conversations + + diff --git a/sdk/flutter/v4/setup.mdx b/sdk/flutter/v4/setup.mdx new file mode 100644 index 000000000..877c9e0e6 --- /dev/null +++ b/sdk/flutter/v4/setup.mdx @@ -0,0 +1,246 @@ +--- +title: "Setup" +sidebarTitle: "Setup" +description: "Install, configure, and initialize the CometChat Flutter SDK in your application." +--- + +{/* TL;DR for Agents and Quick Reference */} + + +```yaml +# Install (pubspec.yaml) +cometchat_sdk: ^4.0.33 +``` + +```dart +import 'package:cometchat_sdk/cometchat_sdk.dart'; + +AppSettings appSettings = (AppSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.allUsers + ..region = "APP_REGION" + ..autoEstablishSocketConnection = true +).build(); + +CometChat.init("APP_ID", appSettings, + onSuccess: (msg) => debugPrint("Init success"), + onError: (e) => debugPrint("Init failed: ${e.message}") +); + +// Login (dev only) +CometChat.login("UID", "AUTH_KEY", + onSuccess: (user) => debugPrint("Login success"), + onError: (e) => debugPrint("Login failed: ${e.message}") +); +``` + +**Prerequisites:** Flutter SDK 1.2+, Android API Level 21+, iOS 11+ +**Credentials:** App ID, Region, Auth Key (dev) or Auth Token (prod) from [CometChat Dashboard](https://app.cometchat.com) + + +## Prerequisites + +| Requirement | Version | +|-------------|---------| +| Flutter SDK | 1.2 or higher | +| Android API Level | 21 or higher | +| AndroidX | Required | +| iOS | 11 or higher | + +Get your credentials from the [CometChat Dashboard](https://app.cometchat.com): + +1. Create a new app +2. Head over to the **API & Auth Keys** section and note the **Auth Key**, **App ID** & **Region** + + +**Auth Key** is for development/testing only. In production, generate **Auth Tokens** on your server using the REST API. Never expose Auth Keys in production client code. + + +## Installation + +Add the following dependency to your `pubspec.yaml` file and run `pub get`: + + + +```yaml +cometchat_sdk: ^4.0.33 +``` + + + +### iOS Setup + +1. Add the following to your Podfile inside the iOS section of your app: + + + +```ruby +post_install do |installer| + +installer.pods_project.targets.each do |target| + +flutter_additional_ios_build_settings(target) + +//Copy from here-------> + +target.build_configurations.each do |build_configuration| + +build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' + +end + +//Copy TILL here-------> + +end + +end +``` + + + + +**Apple Silicon (M1/M2/M3) users:** Excluding `arm64` from the simulator build prevents the app from running natively on Apple Silicon Macs. If you are developing on an Apple Silicon Mac, consider excluding only `i386` instead of `arm64 i386` to enable native simulator builds. + + +2. Change the deployment target to `11` or higher. +3. Navigate to your iOS folder in terminal and run `pod install`. For Apple Silicon systems, use a Rosetta terminal. +4. Set **Enabled Bitcode** to **NO** in the Build Settings of your Xcode project. + + + + + +### Import the SDK + + + + +```dart +import 'package:cometchat_sdk/cometchat_sdk.dart'; +``` + + + +## Initialization + +The `init()` method initializes the SDK and must be called before any other CometChat method. Call it once at app startup, typically in your root widget's `initState()` or `main()` function. + + + +```dart +String region = "APP_REGION"; +String appId = "APP_ID"; + +AppSettings appSettings= (AppSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.allUsers + ..region= region + ..adminHost = "" //optional + ..clientHost = "" //optional + ..autoEstablishSocketConnection = true +).build(); + +CometChat.init(appId, appSettings, + onSuccess: (String successMessage) { + debugPrint("Initialization completed successfully $successMessage"); + }, onError: (CometChatException excep) { + debugPrint("Initialization failed with exception: ${excep.message}"); + } +); +``` + + + +Replace `APP_ID` and `APP_REGION` with your credentials from the [Dashboard](https://app.cometchat.com). + + +`CometChat.init()` must be called before any other SDK method. Calling `login()`, `sendMessage()`, or registering listeners before `init()` will fail. + + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `appId` | `String` | Your CometChat App ID | +| `appSettings` | `AppSettings` | Configuration object built with `AppSettingsBuilder` | +| `onSuccess` | `Function(String)` | Callback triggered on successful initialization | +| `onError` | `Function(CometChatException)` | Callback triggered on initialization failure | + +### AppSettings Options + +| Property | Type | Description | Default | +|----------|------|-------------|---------| +| `subscriptionType` | `String?` | Presence subscription type (`CometChatSubscriptionType.allUsers`, `.roles`, `.friends`) | — | +| `roles` | `List?` | Roles to subscribe to when using role-based presence | — | +| `region` | `String?` | Region where your app was created (`us`, `eu`, `in`) | Required | +| `autoEstablishSocketConnection` | `bool` | Let SDK manage WebSocket connections automatically | `true` | +| `adminHost` | `String?` | Custom admin URL (dedicated deployment) | — | +| `clientHost` | `String?` | Custom client URL (dedicated deployment) | — | + +### Presence Subscription + +Choose how to subscribe to user presence (online/offline status): + +```dart +// All users +AppSettings appSettings = (AppSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.allUsers + ..region = region +).build(); + +// Specific roles +AppSettings appSettings = (AppSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.roles + ..roles = ["admin", "moderator"] + ..region = region +).build(); + +// Friends only +AppSettings appSettings = (AppSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.friends + ..region = region +).build(); +``` + +See [User Presence](/sdk/flutter/user-presence) for more details. + + +**On Success** — A `String` message confirming SDK initialization: + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | `String` | Success confirmation message | `"Initialization completed successfully"` | + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | `String` | Human-readable error message | `"SDK initialization failed."` | +| `details` | `String` | Additional technical details | `"Please verify your App ID and region, then try again."` | + + +### WebSocket Connection + +By default, the SDK manages WebSocket connections automatically. To manage them manually: + +```dart +AppSettings appSettings = (AppSettingsBuilder() + ..region = region + ..autoEstablishSocketConnection = false +).build(); +``` + +See [Connection Behaviour](/sdk/flutter/connection-behaviour) for manual control. + +--- + +## Next Steps + + + + Log in users with Auth Key or Auth Token + + + Send your first message + + diff --git a/sdk/flutter/v4/threaded-messages.mdx b/sdk/flutter/v4/threaded-messages.mdx new file mode 100644 index 000000000..d2b1e40f9 --- /dev/null +++ b/sdk/flutter/v4/threaded-messages.mdx @@ -0,0 +1,536 @@ +--- +title: "Threaded Messages" +sidebarTitle: "Threaded Messages" +description: "Send, receive, and fetch threaded messages using the CometChat Flutter SDK." +--- + + + +| Field | Value | +| --- | --- | +| Key Classes | `TextMessage`, `MediaMessage`, `CustomMessage`, `MessagesRequest`, `MessagesRequestBuilder` | +| Key Methods | `CometChat.sendMessage()`, `CometChat.sendMediaMessage()`, `CometChat.sendCustomMessage()`, `MessagesRequest.fetchPrevious()` | +| Key Properties | `parentMessageId`, `hideReplies` | +| Listener Events | `onTextMessageReceived`, `onMediaMessageReceived`, `onCustomMessageReceived` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Send a message in a thread (attach to parent message) +TextMessage textMessage = TextMessage( + text: "Reply in thread", + receiverUid: "UID", + receiverType: CometChatReceiverType.user, + type: CometChatMessageType.text, +); +textMessage.parentMessageId = 103; // Parent message ID +CometChat.sendMessage(textMessage, onSuccess: (msg) {}, onError: (e) {}); + +// Fetch messages from a thread +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = "UID" + ..parentMessageId = 103 + ..limit = 50).build(); +messageRequest.fetchPrevious(onSuccess: (List list) {}, onError: (e) {}); + +// Exclude threaded messages from main conversation +MessagesRequest request = (MessagesRequestBuilder() + ..uid = "UID" + ..hideReplies = true + ..limit = 50).build(); +``` + + + +Threaded messages (or threads) are messages started from a particular parent message. Each thread is attached to a parent message. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Send Message in a Thread + +As mentioned in the [Send a Message](send-message) section, you can send a message to a User or a Group by mentioning the receiver (uid/guid) and `receiverType`(user/group). + +A message can be categorized as: + +1. Text Message +2. Media Message +3. Custom Message +4. Interactive Message + +Set the `parentMessageId` on the message object to indicate which thread the message belongs to. The id specified in the `parentMessageId` parameter maps the message sent to the particular thread. Any message type — [`TextMessage`](/sdk/reference/messages#textmessage), [`MediaMessage`](/sdk/reference/messages#mediamessage), [`CustomMessage`](/sdk/reference/messages#custommessage), or Interactive Message — can be sent in a thread. + + + + +```dart +String receiverID = "UID"; +String messagesText = "Hello"; +String receiverType = CometChatConversationType.user; +String type = CometChatMessageType.text; + +TextMessage textMessage = TextMessage( + text: messagesText, + receiverUid: receiverID, + receiverType: receiverType, + type: type); +textMessage.parentMessageId = 103; + +CometChat.sendMessage(textMessage, onSuccess: (TextMessage message) { + debugPrint("Message sent successfully: $message"); +}, onError: (CometChatException e) { + debugPrint("Message sending failed with exception: ${e.message}"); +}); +``` + + + + +```dart +String receiverID = "GUID"; +String messagesText = "Hello"; +String receiverType = CometChatConversationType.group; +String type = CometChatMessageType.text; + +TextMessage textMessage = TextMessage( + text: messagesText, + receiverUid: receiverID, + receiverType: receiverType, + type: type); +textMessage.parentMessageId = 103; + +CometChat.sendMessage(textMessage, onSuccess: (TextMessage message) { + debugPrint("Message sent successfully: $message"); +}, onError: (CometChatException e) { + debugPrint("Message sending failed with exception: ${e.message}"); +}); +``` + + + + +`TextMessage` Parameters: + +| Parameter | Type | Description | Required | +| --- | --- | --- | --- | +| `text` | `String` | The text content of the message | Yes | +| `receiverUid` | `String` | The `UID` of the user or `GUID` of the group to send the message to | Yes | +| `receiverType` | `String` | The type of the receiver — `CometChatConversationType.user` or `CometChatConversationType.group` | Yes | +| `type` | `String` | The type of the message — `CometChatMessageType.text` | Yes | +| `parentMessageId` | `int` | The ID of the parent message to send this message as a thread reply | Yes (for threads) | + + +**On Success** — A `TextMessage` object containing all details of the sent threaded message: + + + +**TextMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `601` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#send-thread-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-2"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#send-thread-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `103` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `text` | string | The text content of the message | `"Hello"` | +| `tags` | array | List of tags associated with the message | `[]` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `mentionedUsers` | array | List of mentioned users | `[]` | +| `hasMentionedMe` | boolean | Whether the current user is mentioned | `false` | +| `reactions` | array | List of reactions on the message | `[]` | +| `moderationStatus` | string | Moderation status of the message | `null` | +| `quotedMessageId` | number | ID of the quoted message | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-1"` | +| `name` | string | Display name of the sender | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-2"` | +| `name` | string | Display name of the receiver | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to send the message."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +The above snippet shows how a message with the text "Hello" can be sent in the thread with `parentMessageId` 103. + +Similarly, using the `parentMessageId` parameter, Media and Custom Messages can be sent in threads too. + +### Receiving Real-Time Messages + +The procedure to receive real-time messages is exactly the same as mentioned in the [Receive Messages](receive-messages) section. Use the `MessageListener` class to listen for incoming thread messages. Check if the received message belongs to the active thread using the `parentMessageId` field. + + +Always remove message listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +CometChat.removeMessageListener("listenerId"); +``` + + + + +```dart +class Class_Name with MessageListener { + int activeParentMessageId= 103; +//CometChat.addMessageListener("listenerId", this); + @override +void onTextMessageReceived(TextMessage textMessage) { + if(textMessage.parentMessageId == activeParentMessageId){ + debugPrint("Text message received successfully: $textMessage"); + } + +} + +@override +void onMediaMessageReceived(MediaMessage mediaMessage) { + if(mediaMessage.parentMessageId== activeParentMessageId){ + debugPrint("Media message received successfully: $mediaMessage"); + } +} + +@override +void onCustomMessageReceived(CustomMessage customMessage) { + if(customMessage.parentMessageId== activeParentMessageId){ + debugPrint("Custom message received successfully: $customMessage"); + } +} +} +``` + + + + +### Fetch all the messages for any particular thread. + +Use `MessagesRequestBuilder` with `parentMessageId` to fetch messages belonging to a specific thread. Call `fetchPrevious()` to get messages (max 100 per request), returned as [`BaseMessage`](/sdk/reference/messages#basemessage) objects. Call `fetchPrevious()` again on the same object to get the next set. + + +`MessagesRequestBuilder` Parameters: + +| Parameter | Type | Description | Required | +| --- | --- | --- | --- | +| `uid` | `String` | The `UID` of the user whose conversation thread messages are to be fetched | Yes (for user threads) | +| `guid` | `String` | The `GUID` of the group whose conversation thread messages are to be fetched | Yes (for group threads) | +| `parentMessageId` | `int` | The ID of the parent message whose thread messages are to be fetched | Yes | +| `limit` | `int` | Number of messages to fetch in a single request (max 100) | No | + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..parentMessageId = 103 + ..limit = 50).build(); + +messageRequest.fetchPrevious(onSuccess: (List list) { + debugPrint("Message fetching Successful"); + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + +**On Success** — A `List` containing the fetched thread messages (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `602` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-thread-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-thread-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `103` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +## Avoid Threaded Messages in User/Group Conversations + +While fetching messages for normal user/group conversations using the `MessagesRequest`, the threaded messages by default will be a part of the list of messages received. Use `hideReplies = true` on the `MessagesRequestBuilder` to exclude threaded messages from the list. + + + +```dart +String UID = "cometchat-uid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..uid = UID + ..hideReplies = true + ..limit = 50).build(); + +messageRequest.fetchNext(onSuccess: (List list) { + debugPrint("Message fetching Successful"); + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + +```dart +String GUID = "cometchat-guid-1"; + +MessagesRequest messageRequest = (MessagesRequestBuilder() + ..guid = GUID + ..hideReplies = true + ..limit = 50).build(); + +messageRequest.fetchNext(onSuccess: (List list) { + debugPrint("Message fetching Successful"); + }, onError: (CometChatException e) { + debugPrint("Message fetching failed with exception: ${e.message}"); + }); +``` + + + + + + +**On Success** — A `List` containing the fetched messages excluding threaded replies (each item is a BaseMessage object): + + + +**BaseMessage Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `id` | number | Unique message ID | `603` | +| `metadata` | object | Custom metadata attached to the message | `{}` | +| `receiver` | object | Receiver user object | [See below ↓](#fetch-exclude-thread-receiver-object) | +| `editedBy` | string | UID of the user who edited the message | `null` | +| `conversationId` | string | Unique conversation identifier | `"cometchat-uid-1_user_cometchat-uid-2"` | +| `sentAt` | number | Epoch timestamp when the message was sent | `1745554729` | +| `receiverUid` | string | UID of the receiver | `"cometchat-uid-1"` | +| `type` | string | Type of the message | `"text"` | +| `readAt` | number | Epoch timestamp when the message was read | `0` | +| `deletedBy` | string | UID of the user who deleted the message | `null` | +| `deliveredAt` | number | Epoch timestamp when the message was delivered | `0` | +| `deletedAt` | number | Epoch timestamp when the message was deleted | `0` | +| `replyCount` | number | Number of replies to this message | `0` | +| `sender` | object | Sender user object | [See below ↓](#fetch-exclude-thread-sender-object) | +| `receiverType` | string | Type of the receiver | `"user"` | +| `editedAt` | number | Epoch timestamp when the message was edited | `0` | +| `parentMessageId` | number | ID of the parent message (for threads) | `0` | +| `readByMeAt` | number | Epoch timestamp when read by the current user | `0` | +| `category` | string | Message category | `"message"` | +| `deliveredToMeAt` | number | Epoch timestamp when delivered to the current user | `0` | +| `updatedAt` | number | Epoch timestamp when the message was last updated | `1745554729` | +| `unreadRepliesCount` | number | Count of unread replies | `0` | +| `quotedMessageId` | number | ID of the quoted message | `null` | +| `quotedMessage` | object | The quoted message object | `null` | + +--- + + + +**`sender` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the sender | `"cometchat-uid-2"` | +| `name` | string | Display name of the sender | `"George Alan"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-2.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + +--- + + + +**`receiver` Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the receiver | `"cometchat-uid-1"` | +| `name` | string | Display name of the receiver | `"Andrew Joseph"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | User tags | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745550000` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | string | Human-readable error message | `"Failed to fetch the requested data."` | +| `details` | string | Additional technical details | `"An unexpected error occurred while processing the request."` | + + + +The response is a list of `BaseMessage` objects, excluding any messages that are replies within a thread. Only top-level messages in the conversation are returned. + +--- + +## Next Steps + + + + Learn how to send text, media, and custom messages + + + Handle incoming messages in real-time + + + Add emoji reactions to messages + + + Understand message types and hierarchy + + diff --git a/sdk/flutter/v4/transfer-group-ownership.mdx b/sdk/flutter/v4/transfer-group-ownership.mdx new file mode 100644 index 000000000..d5c67b622 --- /dev/null +++ b/sdk/flutter/v4/transfer-group-ownership.mdx @@ -0,0 +1,95 @@ +--- +title: "Transfer Group Ownership" +sidebarTitle: "Transfer Ownership" +description: "Transfer ownership of a CometChat group to another member using the Flutter SDK." +--- + + + +```dart +// Transfer group ownership to another member +CometChat.transferGroupOwnership( + guid: "GROUP_ID", + uid: "NEW_OWNER_UID", + onSuccess: (String message) => debugPrint("Ownership transferred: $message"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); +``` + +**Note:** Only the current group owner can transfer ownership. The owner must transfer ownership before leaving the group. + + +Transfer ownership of a group to another member. Only the current owner can do this, and since owners cannot leave their group, you must transfer ownership first if you want to leave. The group is represented by a [`Group`](/sdk/reference/entities#group) object and the new owner by a [`User`](/sdk/reference/entities#user) object. See [Leave Group](/sdk/flutter/leave-group). + +## Transfer Ownership + +Use `transferGroupOwnership()` to transfer ownership to another group member. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + + + +```dart +String UID = "cometchat-uid-1"; +String GUID = conversationWith; +CometChat.transferGroupOwnership(guid: GUID,uid: UID, + onSuccess: (String message) { + debugPrint("Owner Transferred Successfully : $message"); + }, + onError: (CometChatException e) { + debugPrint("Owner Transferred failed : ${e.message}"); + }); +``` + + + + + +This method takes the below parameters: + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `guid` | `String` | The GUID of the group for which ownership needs to be transferred | +| `uid` | `String` | The UID of the member who should become the new owner | +| `onSuccess` | `Function(String)` | Callback triggered on successful ownership transfer | +| `onError` | `Function(CometChatException)` | Callback triggered on error | + +On success, the new owner gets admin scope and the previous owner becomes a participant. + + +**On Success** — A `String` message confirming the ownership transfer: + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `message` | string | Success confirmation message | `"cometchat-guid-1 ownership transferred successfully"` | + + + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_NOT_A_MEMBER"` | +| `message` | string | Human-readable error message | `"The target user is not a member of the group."` | +| `details` | string | Additional technical details | `"Ownership can only be transferred to an existing group member."` | + + + +--- + +## Next Steps + + + + Leave a group after transferring ownership + + + Promote or demote group members + + diff --git a/sdk/flutter/v4/transient-messages.mdx b/sdk/flutter/v4/transient-messages.mdx new file mode 100644 index 000000000..9b7654664 --- /dev/null +++ b/sdk/flutter/v4/transient-messages.mdx @@ -0,0 +1,167 @@ +--- +title: "Transient Messages" +sidebarTitle: "Transient Messages" +description: "Send and receive ephemeral real-time messages that are not stored on the server using the CometChat Flutter SDK. Ideal for live reactions and temporary indicators." +--- + + + +| Field | Value | +| --- | --- | +| Key Classes | `TransientMessage` | +| Key Methods | `CometChat.sendTransientMessage()` | +| Receiver Types | `CometChatReceiverType.user`, `CometChatReceiverType.group` | +| Listener Events | `onTransientMessageReceived` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Send transient message (not stored on server) +Map data = {"LIVE_REACTION": "heart"}; +TransientMessage transientMessage = TransientMessage( + receiverId: "UID", + receiverType: CometChatReceiverType.user, + data: data, +); +CometChat.sendTransientMessage(transientMessage, + onSuccess: () => debugPrint("Sent"), + onError: (e) => debugPrint("Error: ${e.message}"), +); + +// Receive transient messages +CometChat.addMessageListener("LISTENER_ID", MessageListener( + onTransientMessageReceived: (TransientMessage message) { + debugPrint("Received: ${message.data}"); + }, +)); +``` + + + +Transient messages are messages that are sent in real-time only and are not saved or tracked anywhere. The receiver of the message will only receive the message if he is online and these messages cannot be retrieved later. + + +**Available via:** SDK | UI Kits + + +## Send a Transient Message + +You can use the `sendTransientMessage()` method to send a transient message to a user or in a group. The receiver will receive this information in the `onTransientMessageReceived()` method of the `MessageListener` class. In order to send the transient message, you need to use the [`TransientMessage`](/sdk/reference/auxiliary#transientmessage) class. + + + +```dart +String receiverId = "cometchat-uid-2"; +Map data = {}; +data["LIVE_REACTION"] = "heart"; + +TransientMessage transientMessage = TransientMessage( + receiverId: receiverId, + receiverType: CometChatReceiverType.user, + data: data, +); + +CometChat.sendTransientMessage(transientMessage, onSuccess: () { + debugPrint("Transient Message Sent"); +}, onError: (CometChatException e) { + debugPrint("Transient message sending failed with exception: ${e.message}"); +}); +``` + + + + +```dart +String receiverId = "cometchat-guid-1"; +Map data = {}; +data["LIVE_REACTION"] = "heart"; + +TransientMessage transientMessage = TransientMessage( + receiverId: receiverId, + receiverType: CometChatReceiverType.group, + data: data, +); + +CometChat.sendTransientMessage(transientMessage, onSuccess: () { + debugPrint("Transient Message Sent"); +}, onError: (CometChatException e) { + debugPrint("Transient message sending failed with exception: ${e.message}"); +}); +``` + + + + + +`TransientMessage` Parameters: + +| Parameter | Type | Description | Required | +| --- | --- | --- | --- | +| `receiverId` | `String` | The `UID` of the user or `GUID` of the group to send the transient message to | Yes | +| `receiverType` | `String` | The type of the receiver — `CometChatReceiverType.user` or `CometChatReceiverType.group` | Yes | +| `data` | `Map` | A map to provide custom data with the transient message | Yes | +| `sender` | `User?` | The sender of the transient message (set automatically by the SDK) | No | + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | `String` | Error code identifier | `"ERR_CHAT_API_FAILURE"` | +| `message` | `String` | Human-readable error message | `"Failed to send the transient message."` | +| `details` | `String` | Additional technical details | `"An unexpected error occurred while sending the transient message."` | + + + +## Real-time Transient Messages + +*In other words, as a recipient, how do I know when someone sends a transient message?* + + +Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +CometChat.removeMessageListener("listenerId"); +``` + + +You will receive the transient message in the `onTransientMessageReceived()` method of the registered `MessageListener` class. + + + +```dart +class Class_Name with MessageListener { + +//CometChat.addMessageListener("listenerId", this); +@override +void onTransientMessageReceived(TransientMessage message) { + // TODO: implement onTransientMessageReceived +} + + +} +``` + + + + + +The received object is a `TransientMessage` with the following fields: + +| Parameter | Type | Description | +| --- | --- | --- | +| `sender` | `User?` | An object of the `User` class holding all the information related to the sender of the transient message. | +| `receiverId` | `String` | Unique ID of the receiver. This can be the `UID` of the user or `GUID` of the group the transient message is sent to. | +| `receiverType` | `String` | The type of the receiver — `CometChatReceiverType.user` or `CometChatReceiverType.group`. | +| `data` | `Map` | A map containing the custom data sent with the transient message. | + +--- + +## Next Steps + + + + Learn how to send persistent text and media messages + + + Show real-time typing status to users + + diff --git a/sdk/flutter/troubleshooting.mdx b/sdk/flutter/v4/troubleshooting.mdx similarity index 100% rename from sdk/flutter/troubleshooting.mdx rename to sdk/flutter/v4/troubleshooting.mdx diff --git a/sdk/flutter/v4/typing-indicators.mdx b/sdk/flutter/v4/typing-indicators.mdx new file mode 100644 index 000000000..19640d53d --- /dev/null +++ b/sdk/flutter/v4/typing-indicators.mdx @@ -0,0 +1,181 @@ +--- +title: "Typing Indicators" +sidebarTitle: "Typing Indicators" +description: "Send and receive real-time typing indicators using the CometChat Flutter SDK." +--- + + + +| Field | Value | +| --- | --- | +| Key Classes | `TypingIndicator` | +| Key Methods | `CometChat.startTyping()`, `CometChat.endTyping()` | +| Receiver Types | `CometChatReceiverType.user`, `CometChatReceiverType.group` | +| Listener Events | `onTypingStarted`, `onTypingEnded` | +| Prerequisites | SDK initialized, user logged in | + +```dart +// Start typing indicator to user +CometChat.startTyping(receiverUid: "UID", receiverType: CometChatReceiverType.user); + +// Start typing indicator to group +CometChat.startTyping(receiverUid: "GUID", receiverType: CometChatReceiverType.group); + +// Stop typing indicator +CometChat.endTyping(receiverUid: "UID", receiverType: CometChatReceiverType.user); + +// Listen for typing indicators +CometChat.addMessageListener("listenerId", MessageListener( + onTypingStarted: (TypingIndicator typingIndicator) { }, + onTypingEnded: (TypingIndicator typingIndicator) { }, +)); +``` + + + +Typing indicators let users know when someone is actively typing a message, creating a more engaging real-time chat experience. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Send a Typing Indicator + +*In other words, as a sender, how do I let the recipient(s) know that I'm typing?* + +### Start Typing + +Use `startTyping()` to notify the receiver that the logged-in user has started typing. The receiver will receive this information in the `onTypingStarted()` method of the `MessageListener` class. + + + +```dart +CometChat.startTyping( + receiverUid: "UID", + receiverType: CometChatReceiverType.user, +); +``` + + + + +```dart +CometChat.startTyping( + receiverUid: "GUID", + receiverType: CometChatReceiverType.group, +); +``` + + + + + +`startTyping()` Parameters: + +| Parameter | Type | Description | Required | +| --- | --- | --- | --- | +| `receiverUid` | `String` | The `UID` of the user or `GUID` of the group to send the typing indicator to | Yes | +| `receiverType` | `String` | The type of the receiver — `CometChatReceiverType.user` or `CometChatReceiverType.group` | Yes | + +`startTyping()` returns `void` — the typing indicator is sent as a fire-and-forget operation. + +### Stop Typing + +Use `endTyping()` to notify the receiver that the logged-in user has stopped typing. The receiver will receive this information in the `onTypingEnded()` method of the `MessageListener` class. + + + +```dart +CometChat.endTyping( + receiverUid: "UID", + receiverType: CometChatReceiverType.user, +); +``` + + + + +```dart +CometChat.endTyping( + receiverUid: "GUID", + receiverType: CometChatReceiverType.group, +); +``` + + + + + +`endTyping()` Parameters: + +| Parameter | Type | Description | Required | +| --- | --- | --- | --- | +| `receiverUid` | `String` | The `UID` of the user or `GUID` of the group to send the typing indicator to | Yes | +| `receiverType` | `String` | The type of the receiver — `CometChatReceiverType.user` or `CometChatReceiverType.group` | Yes | + +`endTyping()` returns `void` — the typing indicator is sent as a fire-and-forget operation. + + +Use the `metadata` field of the `TypingIndicator` class to pass additional custom data along with the typing indicators. The metadata field is a `Map` and can be set using the `.metadata` property. This data will be received at the receiver end and can be obtained using the same property. + + +## Real-time Typing Indicators + +*In other words, as a recipient, how do I know when someone is typing?* + +Use `onTypingStarted` and `onTypingEnded` in `MessageListener` to receive [`TypingIndicator`](/sdk/reference/auxiliary#typingindicator) events. + + +Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. + +```dart +CometChat.removeMessageListener("listenerId"); +``` + + + + +```dart +class Class_Name with MessageListener { + +//CometChat.addMessageListener("listenerId", this); +@override +void onTypingStarted(TypingIndicator typingIndicator) { + // TODO: implement onTypingStarted +} + +@override +void onTypingEnded(TypingIndicator typingIndicator) { + // TODO: implement onTypingEnded +} + + +} +``` + + + + + +The received object is a `TypingIndicator` with the following fields: + +| Parameter | Type | Description | +| --- | --- | --- | +| `sender` | [`User`](/sdk/reference/entities#user) | An object of the `User` class holding all the information related to the sender of the typing indicator. | +| `receiverId` | `String` | `UID` of the receiver. This is the ID of the group or the user the typing indicator is being sent to. | +| `receiverType` | `String` | Indicates if the typing indicator is to a user or a group — `CometChatReceiverType.user` or `CometChatReceiverType.group`. | +| `metadata` | `Map?` | Optional metadata to provide additional data. | +| `lastTimestamp` | `DateTime` | The timestamp of the last typing indicator event. | + +--- + +## Next Steps + + + + Track message delivery and read status + + + Send ephemeral real-time messages like live reactions + + diff --git a/sdk/flutter/v4/update-group.mdx b/sdk/flutter/v4/update-group.mdx new file mode 100644 index 000000000..8371e41af --- /dev/null +++ b/sdk/flutter/v4/update-group.mdx @@ -0,0 +1,128 @@ +--- +title: "Update A Group" +sidebarTitle: "Update Group" +description: "Update group details such as name, description, icon, and metadata using the CometChat Flutter SDK." +--- + + + +```dart +// Update a group +Group group = Group(guid: "GROUP_ID", name: "New Name", type: CometChatGroupType.public); +group.description = "Updated description"; +group.icon = "https://example.com/icon.png"; + +await CometChat.updateGroup( + group: group, + onSuccess: (Group group) => debugPrint("Updated: ${group.name}"), + onError: (CometChatException e) => debugPrint("Error: ${e.message}"), +); +``` + + +Update a group's name, icon, description, or metadata. The GUID and group type cannot be changed after creation. See the [Group Class](/sdk/flutter/create-group#group-class) reference for all editable fields. + +## Update Group + +*In other words, as a group owner, how can I update the group details?* + +You can update the existing details of the group using the `updateGroup()` method. Pass a [`Group`](/sdk/reference/entities#group) object with the updated values. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `group` | `Group` | An instance of the [`Group`](/sdk/reference/entities#group) class with updated values | +| `onSuccess` | `Function(Group group)?` | Callback triggered on successful group update | +| `onError` | `Function(CometChatException excep)?` | Callback triggered on error | + + + +```dart +String GUID = "GUID"; + String groupName = "Hello Group!"; + String groupType = CometChatGroupType.public; + String password = ""; + + Group _group = Group(guid: GUID, name: groupName, type: groupType); + + + await CometChat.updateGroup(group: _group, onSuccess: (Group group ){ + debugPrint("Group Created Successfully : $group "); + }, onError:(CometChatException e) { + debugPrint("Group Creation failed with exception: ${e.message}"); + } ); +``` + + + + + + +**On Success** — A `Group` object containing all details of the updated group: + + + +**Group Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `guid` | string | Unique identifier for the group | `"cometchat-guid-1"` | +| `name` | string | Display name of the group | `"Hello Group!"` | +| `icon` | string | URL of the group icon | `null` | +| `description` | string | Description of the group | `null` | +| `membersCount` | number | Number of members in the group | `5` | +| `metadata` | object | Custom metadata attached to the group | `{}` | +| `joinedAt` | number | Epoch timestamp when the logged-in user joined the group | `1745554729` | +| `hasJoined` | boolean | Whether the logged-in user has joined the group | `true` | +| `createdAt` | number | Epoch timestamp when the group was created | `1745554729` | +| `owner` | string | UID of the group owner | `"cometchat-uid-1"` | +| `updatedAt` | number | Epoch timestamp when the group was last updated | `1745554800` | +| `tags` | array | List of tags associated with the group | `[]` | +| `type` | string | Type of the group (public, private, password) | `"public"` | +| `scope` | string | Scope of the logged-in user in the group | `"admin"` | +| `password` | string | Password for password-protected groups | `null` | +| `isBannedFromGroup` | boolean | Whether the logged-in user is banned from the group | `false` | + + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_GUID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified group does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid GUID for the group."` | + + + +After the successful update of the group, you will receive an instance of `Group` class containing updated information of the group. + +For more information on the `Group` class, please check [here](/sdk/flutter/create-group#group-class). + + +There is no real-time event listener for group updates. To get the latest group information after calling `updateGroup()`, fetch the group details again using `getGroup()`. + + +--- + +## Next Steps + + + + Permanently delete a group + + + Fetch and filter groups with pagination + + + Create new public, private, or password-protected groups + + + Overview of all group management features + + diff --git a/sdk/flutter/upgrading-from-v3-guide.mdx b/sdk/flutter/v4/upgrading-from-v3-guide.mdx similarity index 100% rename from sdk/flutter/upgrading-from-v3-guide.mdx rename to sdk/flutter/v4/upgrading-from-v3-guide.mdx diff --git a/sdk/flutter/v4/user-management.mdx b/sdk/flutter/v4/user-management.mdx new file mode 100644 index 000000000..95622f92c --- /dev/null +++ b/sdk/flutter/v4/user-management.mdx @@ -0,0 +1,282 @@ +--- +title: "User Management" +sidebarTitle: "User Management" +description: "Create, update, and manage CometChat users programmatically using the Flutter SDK. Includes user creation, profile updates, and the User class reference." +--- + + + +```dart +// Create a user +User user = User(uid: "user1", name: "Kevin"); +CometChat.createUser(user, "AUTH_KEY", onSuccess: (User user) { + debugPrint("User created: ${user.name}"); +}, onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); +}); + +// Update a user (requires API Key) +User updatedUser = User(uid: "user1", name: "Kevin Fernandez"); +CometChat.updateUser(updatedUser, "AUTH_KEY", onSuccess: (User user) { + debugPrint("User updated: ${user.name}"); +}, onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); +}); + +// Update logged-in user (no auth key needed) +User currentUser = User(name: "New Name"); +CometChat.updateCurrentUserDetails(currentUser, onSuccess: (User user) { + debugPrint("User updated: ${user.name}"); +}, onError: (CometChatException e) { + debugPrint("Error: ${e.message}"); +}); +``` + +**Note:** User creation/deletion should ideally happen on your backend via the [REST API](https://api-explorer.cometchat.com). + + +Users must exist in CometChat before they can log in. This page covers creating, updating, and deleting users. All methods that return user data return a [`User`](/sdk/reference/entities#user) object. + +The typical flow: +1. User registers in your app → Create user in CometChat +2. User logs into your app → [Log user into CometChat](/sdk/flutter/authentication-overview) + + +User deletion is only available via the [REST API](https://api-explorer.cometchat.com/reference/delete-user) — there is no client-side SDK method for it. + + +## Creating a User + +User creation should ideally happen on your backend via the [REST API](https://api-explorer.cometchat.com/reference/creates-user). + + +**Security:** Never expose your `Auth Key` in client-side production code. User creation and updates using `Auth Key` should ideally happen on your backend server. Use client-side creation only for prototyping or development. + + +For client-side creation (development only), use `createUser()`: + + + +```dart +String authKey = "AUTH_KEY"; // Replace with the auth key of app +User user = User(uid: "usr1", name: "Kevin"); // Replace with name and uid of user + +CometChat.createUser(user, authKey, onSuccess: (User user) { + debugPrint("Create User successful $user"); +}, onError: (CometChatException e) { + debugPrint("Create User Failed with exception ${e.message}"); +}); +``` + + + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `user` | `User` | A `User` object containing the details of the user to be created. The `uid` and `name` fields are **required**. | +| `authKey` | `String` | Your CometChat Auth Key. Use only for development — never expose in production client code. | +| `onSuccess` | `Function(User user)` | Callback triggered on successful user creation, returning the created `User` object. | +| `onError` | `Function(CometChatException excep)` | Callback triggered on failure, returning a `CometChatException` with error details. | + +Returns a [`User`](/sdk/reference/entities#user) object. See [User Class](#user-class) for all available fields. + + +**On Success** — A `User` object containing all details of the created user: + + + +**User Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"usr1"` | +| `name` | string | Display name of the user | `"Kevin"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `null` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | List of tags associated with the user | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `0` | + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified UID does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid UID for the user."` | + + + + +UID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. + + + +## Updating a User + +Like creation, user updates should ideally happen on your backend via the [REST API](https://api-explorer.cometchat.com/reference/update-user). + +For client-side updates (development only), use `updateUser()`: + + + +```dart +String apiKey = "AUTH_KEY"; // Replace with the auth key of app +User user = User(uid: "usr1", name: "Kevin Fernandez"); + +CometChat.updateUser(user, apiKey, onSuccess: (User user) { + debugPrint("User updated: $user"); +}, onError: (CometChatException e) { + debugPrint("Update User Failed with exception ${e.message}"); +}); +``` + + + +Ensure the [`User`](/sdk/reference/entities#user) object has the correct `UID` set. + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `user` | `User` | A `User` object with the `uid` of the user to update and the fields to change. | +| `apiKey` | `String` | Your CometChat Auth Key. Use only for development — never expose in production client code. | +| `onSuccess` | `Function(User retUser)` | Callback triggered on successful update, returning the updated `User` object. | +| `onError` | `Function(CometChatException excep)` | Callback triggered on failure, returning a `CometChatException` with error details. | + +Returns a [`User`](/sdk/reference/entities#user) object. See [User Class](#user-class) for all available fields. + + +**On Success** — A `User` object containing all details of the updated user: + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"usr1"` | +| `name` | string | Display name of the user | `"Kevin Fernandez"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `null` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"offline"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | List of tags associated with the user | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `0` | + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified UID does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid UID for the user."` | + + +## Updating Logged-in User + +Use `updateCurrentUserDetails()` to update the current user without an Auth Key. Note: You cannot update the user's role with this method. + + + +```dart +User user = User(name: 'Updated Name'); + +CometChat.updateCurrentUserDetails(user, onSuccess: (User updatedUser) { + debugPrint("Updated User: $updatedUser"); +}, onError: (CometChatException e) { + debugPrint("Updated User exception : ${e.message}"); +}); +``` + + + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `user` | `User` | A `User` object with the fields to update. The `uid` is ignored — only the logged-in user is updated. | +| `onSuccess` | `Function(User retUser)` | Callback triggered on successful update, returning the updated `User` object. | +| `onError` | `Function(CometChatException excep)` | Callback triggered on failure, returning a `CometChatException` with error details. | + +The method returns a [`User`](/sdk/reference/entities#user) object. + + +**On Success** — A `User` object containing all details of the updated user: + + + +**User Object:** + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `uid` | string | Unique identifier of the user | `"cometchat-uid-1"` | +| `name` | string | Display name of the user | `"Updated Name"` | +| `link` | string | Profile link | `null` | +| `avatar` | string | Avatar URL | `"https://assets.cometchat.io/sampleapp/v2/users/cometchat-uid-1.webp"` | +| `metadata` | object | Custom metadata | `{}` | +| `status` | string | Online status | `"online"` | +| `role` | string | User role | `"default"` | +| `statusMessage` | string | Status message | `null` | +| `tags` | array | List of tags associated with the user | `[]` | +| `hasBlockedMe` | boolean | Whether this user has blocked the current user | `false` | +| `blockedByMe` | boolean | Whether the current user has blocked this user | `false` | +| `lastActiveAt` | number | Epoch timestamp of last activity | `1745554700` | + + + + +| Parameter | Type | Description | Sample Value | +|-----------|------|-------------|--------------| +| `code` | string | Error code identifier | `"ERR_UID_NOT_FOUND"` | +| `message` | string | Human-readable error message | `"The specified UID does not exist."` | +| `details` | string | Additional technical details | `"Please provide a valid UID for the user."` | + + +By using the `updateCurrentUserDetails()` method one can only update the logged-in user irrespective of the UID passed. Also, it is not possible to update the role of a logged-in user. + +## Deleting a User + +User deletion is only available via the [REST API](https://api-explorer.cometchat.com/reference/delete-user). + +## User Class + +| Field | Editable | Information | +| ------------- | --------------------------------------------------- | -------------------------------------------------------------------- | +| uid | specified on user creation. Not editable after that | Unique identifier of the user | +| name | Yes | Display name of the user | +| avatar | Yes | URL to profile picture of the user | +| link | Yes | URL to profile page | +| role | Yes | User role of the user for role based access control | +| metadata | Yes | Additional information about the user as JSON | +| status | No | Status of the user. Could be either online/offline | +| statusMessage | Yes | Any custom status message that needs to be set for a user | +| lastActiveAt | No | The unix timestamp of the time the user was last active. | +| hasBlockedMe | No | A boolean that determines if the user has blocked the logged in user | +| blockedByMe | No | A boolean that determines if the logged in user has blocked the user | +| tags | Yes | A list of tags to identify specific users | + +## Next Steps + + + + Fetch and filter user lists with pagination. + + + Monitor real-time online/offline status. + + + Block and unblock users. + + + Log users into CometChat. + + diff --git a/sdk/flutter/v4/user-presence.mdx b/sdk/flutter/v4/user-presence.mdx new file mode 100644 index 000000000..c1f0fecea --- /dev/null +++ b/sdk/flutter/v4/user-presence.mdx @@ -0,0 +1,152 @@ +--- +title: "User Presence" +sidebarTitle: "User Presence" +description: "Track when users come online or go offline in real-time using CometChat's presence subscription system." +--- + +{/* TL;DR for Agents and Quick Reference */} + + +```dart +// Configure presence subscription during init +AppSettings appSettings = (AppSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.allUsers // or .roles, .friends + ..region = "REGION" + ..autoEstablishSocketConnection = true +).build(); + +await CometChat.init("APP_ID", appSettings, onSuccess: (msg) {}, onError: (e) {}); + +// Listen for presence changes +CometChat.addUserListener("UNIQUE_LISTENER_ID", UserListener( + onUserOnline: (User user) { + debugPrint("${user.name} is online"); + }, + onUserOffline: (User user) { + debugPrint("${user.name} is offline"); + }, +)); + +// Remove listener when done +CometChat.removeUserListener("UNIQUE_LISTENER_ID"); +``` + + +Track whether users are online or offline in real-time. + + +**Available via:** SDK | [REST API](https://api-explorer.cometchat.com) | [UI Kits](/ui-kit/flutter/overview) + + +## Real-time Presence + +*In other words, as a logged-in user, how do I know if a user is online or offline?* + +Configure presence subscription in `AppSettings` during SDK initialization. The `AppSettingsBuilder` provides three subscription options: + +| Method | Description | +| ------ | ----------- | +| `subscribePresenceForAllUsers()` | Receive presence updates for all users | +| `subscribePresenceForRoles(List roles)` | Receive presence updates only for users with specified roles | +| `subscribePresenceForFriends()` | Receive presence updates only for friends | + +If none of these methods are called, no presence events will be delivered. + + +You must configure presence subscription in `AppSettings` during `CometChat.init()` before any presence events will be delivered. See [Setup SDK](/sdk/flutter/setup) for details. + + +Register a `UserListener` to receive presence events. We suggest adding the listener in the `init` method of the activity or the fragment where you wish to receive these events in. + + + +```dart +class Class_Name with UserListener { + +//CometChat.addUserListener("user_Listener_id", this); + @override +void onUserOnline(User user) { + // TODO: implement onUserOnline +} +@override +void onUserOffline(User user) { + // TODO: implement onUserOffline +} + + +} +``` + + + + + +| Parameter | Description | +| ------------ | ----------------------------------------------------------------------------------------------- | +| `listenerID` | An ID that uniquely identifies that listener. We recommend using the activity or fragment name. | + +### UserListener Events + +| Event | Description | +| ----- | ----------- | +| `onUserOnline(User user)` | Triggered when a subscribed user comes online | +| `onUserOffline(User user)` | Triggered when a subscribed user goes offline | + +Each callback receives a [`User`](/sdk/reference/entities#user) object with presence information. + +Relevant fields to access on returned users: + +| Field | Type | Description | +|-------|------|-------------| +| `status` | `String` | Online status of the user (`"online"` or `"offline"`) | +| `lastActiveAt` | `int` | Timestamp when the user was last active | + +Remove the listener when no longer needed: + + + +```dart +String listenerID = "UNIQUE_LISTENER_ID"; + +CometChat.removeUserListener(listenerID); +``` + + + + + + +Always remove listeners when they're no longer needed (e.g., in the `dispose()` method). Failing to remove listeners can cause memory leaks and duplicate event handling. + + +## User List Presence + +*In other words, as a logged-in user, when I retrieve the user list, how do I know if a user is online/offline?* + +When you [retrieve the list of users](/sdk/flutter/retrieve-users), in the [User](/sdk/flutter/user-management) object, you will receive 2 keys: + +1. `status` - This will hold either of the two values : + +* `online` - This indicates that the user is currently online and available to chat. +* `offline` - This indicates that the user is currently offline and is not available to chat. + +2. `lastActiveAt` - In case the user is offline, this field holds the timestamp of the time when the user was last online. This can be used to display a **Last seen** for that user. + +--- + +## Next Steps + + + + Fetch user lists with filtering and pagination + + + Create and update users programmatically + + + Monitor SDK connection to CometChat servers + + + Block and unblock users in your app + + diff --git a/sdk/flutter/v4/users-overview.mdx b/sdk/flutter/v4/users-overview.mdx new file mode 100644 index 000000000..32e83bbb2 --- /dev/null +++ b/sdk/flutter/v4/users-overview.mdx @@ -0,0 +1,37 @@ +--- +title: "Users" +sidebarTitle: "Overview" +description: "Overview of CometChat user functionality including user management, retrieval, and presence tracking in the Flutter SDK." +--- + + + +- [User Management](/sdk/flutter/user-management) — Create and update users +- [Retrieve Users](/sdk/flutter/retrieve-users) — Fetch and filter user lists +- [User Presence](/sdk/flutter/user-presence) — Track online/offline status +- [Block Users](/sdk/flutter/block-users) — Block and unblock users + + +Every person who uses your app needs a corresponding user in CometChat. Once a user exists, you can manage their profile, fetch user lists for your UI, track who's online, and control communication with blocking. + +- [User Management](/sdk/flutter/user-management) — Create users when they sign up, update profiles, and delete accounts +- [Retrieve Users](/sdk/flutter/retrieve-users) — Fetch and filter user lists with pagination, search, and role-based filtering +- [User Presence](/sdk/flutter/user-presence) — Monitor real-time online/offline status and subscribe to presence changes +- [Block Users](/sdk/flutter/block-users) — Block and unblock users to prevent all communication + +## Next Steps + + + + Create, update, and delete users in CometChat. + + + Fetch user lists with filtering, sorting, and pagination. + + + Monitor real-time online/offline status of users. + + + Block and unblock users to control communication. + + diff --git a/sdk/flutter/v4/webhooks-overview.mdx b/sdk/flutter/v4/webhooks-overview.mdx new file mode 100644 index 000000000..d52007a59 --- /dev/null +++ b/sdk/flutter/v4/webhooks-overview.mdx @@ -0,0 +1,115 @@ +--- +title: "Webhooks" +sidebarTitle: "Webhooks" +description: "Configure server-side webhooks to receive real-time notifications for messages, users, groups, calls, and moderation events in your Flutter application." +url: "/fundamentals/webhooks" +--- + + + +Webhooks send HTTP POST requests to your server when events occur: + +**Setup Requirements:** +- HTTPS endpoint (SSL required) +- Publicly accessible URL +- Support POST method with `application/json` +- Return HTTP 200 OK to acknowledge receipt + +**Event Categories:** +- **Messages:** `message_sent`, `message_edited`, `message_deleted`, `message_read_receipt` +- **Users:** `user_blocked`, `user_unblocked`, `user_connection_status_changed` +- **Groups:** `group_created`, `group_member_added`, `group_member_left` +- **Calls:** `call_initiated`, `call_started`, `call_ended`, `recording_generated` +- **Moderation:** `moderation_engine_approved`, `moderation_engine_blocked` + +**Configure via:** [CometChat Dashboard](https://app.cometchat.com) → Your App → Webhooks + + +CometChat Webhooks enable real-time, event-driven communication with your server by sending HTTP POST requests for specific events such as messages, user actions, group updates, calls, and moderation results. + +You can use webhooks to build custom workflows such as sending SMS or email notifications, logging activity, syncing with external systems, or triggering automation. + + +Webhooks are configured at the application level through the CometChat Dashboard, not within the Flutter SDK. The SDK handles real-time events via listeners, while webhooks deliver events to your backend server. + + +--- + +## When to Use Webhooks vs SDK Listeners + +| Use Case | Solution | +| --- | --- | +| Real-time updates in Flutter app | SDK Listeners (`CometChatMessageEvents`, `CometChatUserEvents`) | +| Server-side processing | Webhooks | +| Push notifications to offline users | Webhooks | +| Analytics and logging | Webhooks | +| Third-party integrations | Webhooks | +| Syncing with external databases | Webhooks | + +--- + +## Setting Up Webhooks + +Webhooks are configured through the CometChat Dashboard: + + + + Go to [CometChat Dashboard](https://app.cometchat.com) → Your App → Webhooks + + + Click **Create Webhook** and enter your HTTPS endpoint URL + + + Choose which events should trigger webhook calls (messages, users, groups, calls, moderation) + + + Set up Basic Authentication with username/password for security + + + +--- + +## Webhook Endpoint Requirements + +Your webhook endpoint must meet these criteria: + +1. **Use HTTPS** – All webhook URLs must be secured with SSL +2. **Be publicly accessible** – Your server should be reachable from the internet +3. **Support POST method** – Events are delivered as `HTTP POST` requests with `application/json` content +4. **Return HTTP 200 OK** – Acknowledge receipt within 2 seconds + +--- + +## Best Practices + + + Enable `retryOnFailure` when setting up webhooks. CometChat retries failed deliveries after 10 seconds, then 30 seconds. Use unique event IDs to deduplicate retries. + + + For long processing tasks, enqueue events to systems like Kafka, RabbitMQ, or AWS SQS, and process them asynchronously. Respond within 2 seconds to prevent timeouts. + + + Return appropriate HTTP status codes: `200 OK` for success, `4xx` for client errors, `5xx` for server issues. + + + Maintain detailed logs of incoming webhook requests and responses. Track failures, latency, and retry attempts. + + +--- + +## Next Steps + + + + Handle events directly in your Flutter app with SDK listeners + + + Explore additional SDK resources and documentation + + + View all webhook event payloads and details + + + Learn how to create and manage webhooks via REST API + + diff --git a/sdk/flutter/v5/additional-message-filtering.mdx b/sdk/flutter/v5/additional-message-filtering.mdx deleted file mode 100644 index 1d9687d68..000000000 --- a/sdk/flutter/v5/additional-message-filtering.mdx +++ /dev/null @@ -1,598 +0,0 @@ ---- -title: "Additional Message Filtering" ---- - - - -The `MessagesRequest` class as you must be familiar with helps you to fetch messages based on the various parameters provided to it. This document will help you understand better the various options that are available using the `MessagesRequest` class. - -The `MessagesRequest` class is designed using the `Builder design pattern`. In order to obtain an object of the `MessagesRequest` class, you will have to make use of the `MessagesRequestBuilder` class in the `MessagesRequest` class. - -The `MessagesRequestBuilder` class allows you to set various parameters to the `MessagesRequest` class based on which the messages are fetched. - -Steps to generate an object of the MessagesRequest class: - -1. Create an object of the `MessagesRequestBuilder` class. -2. Set all the parameters you wish to set. -3. Call the `build()` method of the `MessagesRequestBuilder` class to get an object of the `MessagesRequest` class. - -Once you have an object of the `MessagesRequest` class, you can call either the `fetchNext()` method or the `fetchPrevious()` method using the object. - -1. fetchNext() - Calling this method will return the messages after the specified parameters. -2. fetchPrevious() - Calling this method will give you messages before the specified parameters. - -Since messages are obtained in a paginated manner, a `maximum of 100` messages can be pulled in a single iteration. Calling the `fetchPrevious()`/`fetchNext()` method on the same `MessagesRequest` object will get you the next set of messages. - -Now that you are clear how to use the `MessagesRequest` class, below are the various options available: - -## Number of messages fetched - -*In other words, how do I set the number of messages fetched in a single iteration* - -To achieve this, you can use the `setLimit()` method. This method takes an integer value as the input and informs the SDK to fetch the specified number of messages in one iteration. The maximum number of messages that can be fetched in one go is `100`. The default limit is `30`. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..limit = 30).build(); -``` - - - - - -## Direct Page Navigation - -*In other words, how do I jump to a specific page of messages* - -You can use the `setPage()` method to directly navigate to a specific page of messages. This enables direct page navigation and improved data handling in paginated responses. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..limit = 30 - ..page = 2).build(); -``` - - - - - -## Messages for a user conversation - -*In other words, how do I fetch messages between me and any user* - -This can be achieved using the `UID` parameter. This method takes the UID of the user with whom the conversation is to be fetched. When a valid UID is passed, the SDK will return all the messages that are a part of the conversation between the logged-in user and the UID that has been specified. - - - -```dart -String UID = "cometchat-uid-1"; -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..limit = 50).build(); -``` - - - - - -## Messages for a group conversation - -*In other words, how do I fetch messages for any group conversation* - -You can achieve this using the `GUID` method. This method takes the GUID of a group for which the conversations are to be fetched. Passing a valid GUID to this method will return all the messages that are a part of the group conversation. Please note that the logged-in user must be a member of the group to fetch the messages for that group. - - - -```dart -String GUID = "cometchat-uid-1"; -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..guid = GUID - ..limit = 50).build(); -``` - - - - - -## Messages before/after a message - -*In other words, how do I fetch messages before or after a particular message* - -This can be achieved using the `messageId` parameter. This method takes the messageId as input and provides messages only after/before the message-id based on if the fetchNext() or fetchPrevious() method is triggered. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..messageId = 50 - ..limit = 50).build(); -``` - - - - - -This method can be used along with `UID` or `GUID` parameter to fetch messages after/before any specific message-id for a particular user/group conversation. - -## Messages before/after a given time - -*In other words, how do I fetch messages before or after a particular date or time* - -You can easily achieve this using the `timestamp` parameter . This method takes the `DateTime` timestamp as input and provides messages only after/before the timestamp based on if fetchNext() or fetchPrevious() method is triggered. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..timestamp = DateTime.now() - ..limit = 50).build(); -``` - - - - - -This method can be used along with `UID` or `UID` methods to fetch messages after/before any specific date or time for a particular user/group conversation. - -## Unread messages - -*In other words, how do I fetch unread messages* - -This can easily be achieved using setting the unread flag to true. For this, you need to use the unread parameter. This method takes a boolean value as input. If the value is set to true, the SDK will return just the unread messages. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..unread = true - ..limit = 50).build(); -``` - - - - - -This method along with `GUID` or `UID` can be used to fetch unread messages for a particular group or user conversation respectively. - -## Exclude messages from blocked users - -*In other words, how do I fetch messages excluding the messages from the users I have blocked* - -This can be easily achieved using the `hideMessagesFromBlockedUsers` parameter. This method accepts a boolean value which determines if the messages from users blocked by the logged-in user need to be a part if the fetched messages. If the value is set to true, the messages will be hidden and won't be a part of the messages fetched. The default value is false i.e if this method is not used, the messages from blocked users will be included in the fetched messages. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hideMessagesFromBlockedUsers = true - ..limit = 50).build(); -``` - - - - - -This parameter can be used to hide the messages by users blocked by logged in user in groups that both the members are a part of. - -## Updated and received messages - -*In other words, how do I fetch messages that have been received or updated after a particular date or time* - -This method accepts a `DateTime` timestamp value and will return all the messages that have been updated and the ones that have been sent/received after the specified time. The messages updated could mean the messages that have been marked as read/delivered or if the messages are edited or deleted. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..updatedAfter = DateTime.now() - ..limit = 50).build(); -``` - - - - - -This can be useful in finding the messages that have been received or updated after a certain time. It can prove very useful if you are saving the messages locally and would like to know if the messages that have been updated or received after the last message is available in your local databases. - -## Updated messages only - -*In other words, how do I fetch messages that have been updated after a particular date or time* - -This can be achieved easily by setting the updatesOnly parameter to true. To do so, you can use the updatesOnly() method. This method takes a boolean input and can be used with the `updatedAfter` parameter to get just the updated messages and not the messages sent/received after the specified time. This method cannot be used independently and always needs to be used with the `updatedAfter` parameter. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..updatedAfter = DateTime.now() - ..updatesOnly = true - ..limit = 50).build(); -``` - - - - - -## Messages for multiple categories - -*In other words, how do I fetch messages belonging to multiple categories* - -We recommend before trying this, you refer to the [Message Structure and Hierarchy](/sdk/flutter/v5/messaging-overview#message-structure-and-hierarchy) guide to get familiar with the various categories of messages. - -For this, you will have to use the `categories` method. This method accepts a list of categories. This tells the SDK to fetch messages only belonging to these categories. - - - -```dart -String UID = "cometchat-uid-1"; -List categories = []; -categories.add("message"); -categories.add("custom"); - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..categories = categories - ..limit = 50).build(); -``` - - - - - -The above snippet will help you get only the messages belonging to the `message` and `custom` category. This can also be used to disable certain categories of messages like `call` and `action`. This along with `UID` and GUID can help display only the messages you wish to display avoiding the other category of messages. - -## Messages for multiple types - -*In other words, how do I fetch messages belonging to multiple types* - -We recommend you refer to the [Message Structure and Hierarchy](/sdk/flutter/v5/messaging-overview#message-structure-and-hierarchy) guide to get familiar with the various types of messages before trying this out. - -This can be easily achieved using the `types` parameter, which accepts a list of types. This tells the SDK to fetch messages only belonging to these types. - - - -```dart -String UID = "cometchat-uid-1"; -List types = []; -types.add("image"); -types.add("video"); - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..types = types - ..limit = 50).build(); -``` - - - - - -Using the above code snippet, you can fetch all the media messages. This along with UID or GUID can be used to fetch media messages for any particular conversation. This can be useful in many other scenarios as well. - -## Messages for a specific thread - -*In other words, how do I fetch messages that are a part of a thread and not directly a user/group conversations* - -This can be done using the `parentMessageId` parameter. This parameter needs to be used when you have implemented threaded conversations in your app. This method will return the messages only belonging to the thread with the specified parent Id. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..parentMessageId = 103 - ..limit = 50).build(); -``` - - - - - -The above code snippet returns the messages that belong to the thread with parent id 103. - -## Hide threaded messages in user/group conversations - -*In other words, how do I exclude threaded messages from the normal user/group conversations* - -In order to do this, you can use the `hideReplies` parameter. This parameter is also related to threaded conversations. This method takes boolean as input. This boolean when set to true will make sure that the messages that belong to threads are not fetched. If set to false, which is also the default value, the messages belong to the threads will also be fetched along with other messages. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hideReplies = true - ..limit = 50).build(); -``` - - - - - -## Hide deleted messages in user/group conversations - -*In other words, how do I exclude deleted messages a user/group conversations* - -In order to do this, you can use the hideDeleted parameter. This parameter takes boolean as input. If set to true, it will make sure that the deleted messages are not fetched. If set to false, which is also the default value, the deleted messages will also be fetched along with other messages. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hideDeleted = true - ..limit = 50).build(); -``` - - - - - -## Messages by tags - -*In other words, how do I fetch messages belonging to specific tags* - -In order to do this, you can use the `tags` parameter. This parameter accepts a list of tags. This tells the SDK to fetch messages only belonging to these tags. - - - -```dart -String UID = "cometchat-uid-1"; -List tags = []; -tags.add("tag1"); -tags.add("tag2"); - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..tags = tags - ..limit = 50).build(); -``` - - - - - -## Messages with tags - -*In other words, how do I fetch messages with the tags information* - -In order to do this, you can use the `withTags` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages along with the tags information. When set to `false` , the SDK will not fetch tags information associated with messages. The default value for this parameter is `false` . - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ...withTags = true - ..limit = 50).build(); -``` - - - - - -## Messages with links - -In other words, as a logged-in user, how do I fetch messages that contains links? - -In order to do this, you can use the `hasLinks` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages which have links in the text. The default value for this parameter is `false`. - - - -This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) - - - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hasLinks = true - ..limit = 50).build(); -``` - - - - - -## Messages with attachments - -In other words, as a logged-in user, how do I fetch messages that contains attachments? - -In order to do this, you can use the `hasAttachments` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages which have attachments (image, audio, video or file). The default value for this parameter is `false`. - - - -This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) - - - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hasAttachments = true - ..limit = 50).build(); -``` - - - - - -## Messages with reactions - -In other words, as a logged-in user, how do I fetch messages that contains reactions? - -In order to do this, you can use the `hasReactions` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages which have reactions. The default value for this parameter is `false`. - - - -This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) - - - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hasReactions = true - ..limit = 50).build(); -``` - - - - - -## Messages with mentions - -In other words, as a logged-in user, how do I fetch messages that contains mentions? - -In order to do this, you can use the `hasMentions` parameter. This parameter accepts boolean as input. When set to `true` , the SDK will fetch messages which have mentions. The default value for this parameter is `false`. - - - -This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) - - - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hasMentions = true - ..limit = 50).build(); -``` - - - - - -## Messages with particular user mentions - -In other words, as a logged-in user, how do I fetch messages that mentions specific users? - -In order to do this, you can use the `mentionedUids` parameter. This parameter accepts a list of UIDs. When set, the SDK will fetch messages which have the mentions of the UIDs passed. - - - -This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) - - - - - -```dart -String UID = "cometchat-uid-1"; -List mentionedUIDs = []; -tags.add("cometchat-uid-1"); -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..mentionedUids = mentionedUIDs - ..limit = 50).build(); -``` - - - - - - -## Messages by attachment types - -*In other words, how do I fetch messages with specific attachment types* - -In order to do this, you can use the `attachmentTypes` parameter. This parameter accepts a list of attachment types. This tells the SDK to fetch messages only with the specified attachment types. - - - -```dart -String UID = "cometchat-uid-1"; -List attachmentTypes = []; -attachmentTypes.add("image"); -attachmentTypes.add("video"); - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..attachmentTypes = attachmentTypes - ..limit = 50).build(); -``` - - - - - -The available attachment types are: -- `image` - Image attachments -- `video` - Video attachments -- `audio` - Audio attachments -- `file` - File/document attachments - -## Hide quoted messages - -*In other words, how do I exclude quoted messages from the response* - -In order to do this, you can use the `hideQuotedMessages` parameter. This parameter accepts boolean as input. When set to `true`, the SDK will not include the quoted message object in the response. The default value for this parameter is `false`. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hideQuotedMessages = true - ..limit = 50).build(); -``` - - - - - -This can be useful when you want to reduce the payload size and don't need the full quoted message details in the response. diff --git a/sdk/flutter/v5/ai-agents.mdx b/sdk/flutter/v5/ai-agents.mdx deleted file mode 100644 index 1ee853823..000000000 --- a/sdk/flutter/v5/ai-agents.mdx +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: "AI Agents" ---- - -# AI Agents Overview - -AI Agents enable intelligent, automated interactions within your application. They can process user messages, trigger tools, and respond with contextually relevant information. For a broader introduction, see the [AI Agents section](/ai-agents). - -> **Note:** -> Currently, an Agent only responds to **Text Messages**. - -## Agent Run Lifecycle and Message Flow - -This section explains how a user’s text message to an Agent becomes a structured "run" which emits real-time events and then produces agentic messages for historical retrieval. -- A user sends a text message to an Agent. -- The platform starts a run and streams real-time events via the **`AIAssistantListener`**. -- After the run completes, persisted Agentic Messages arrive via the **`MessageListener`**. - -### Real-time Events -Events are received via the **`onAIAssistantEventReceived`** method of the **`AIAssistantListener`** class in this general order: - -1. Run Start -2. Zero or more tool call cycles (repeats for each tool invocation): - - Tool Call Start - - Tool Call Arguments - - Tool Call End - - Tool Call Result -3. One or more assistant reply streams: - - Text Message Start - - Text Message Content (multiple times; token/char streaming) - - Text Message End -4. Run Finished - -Notes: -- `Run Start` and `Run Finished` are always emitted. -- `Tool Call` events appear only when a backend or frontend tool is invoked. There can be multiple tool calls in a single run. -- `Text Message` events are always emitted and carry the assistant’s reply incrementally. - - - - ```dart -import 'package:flutter/foundation.dart'; -import 'package:cometchat_sdk/cometchat_sdk.dart'; - -class AIAssistantEventHandler with AIAssistantListener { - final String _sdkStreamListenerId = "unique_listener_id"; - - /// Call this to start listening to AI Assistant events - void addListener() { - CometChat.addAIAssistantListener(_sdkStreamListenerId, this); - debugPrint("AIAssistantListener added successfully."); - } - - /// Call this to remove the AI Assistant listener - void removeListener() { - CometChat.removeAIAssistantListener(_sdkStreamListenerId); - debugPrint("AIAssistantListener removed successfully."); - } - - @override - void onAIAssistantEventReceived(AIAssistantBaseEvent aiAssistantBaseEvent) { - debugPrint( - "Received AI Event: ${aiAssistantBaseEvent.type} for Run ID: ${aiAssistantBaseEvent.id}", - ); - } -} - ``` - - - -#### Event descriptions -- Run Start: A new run has begun for the user’s message. -- Tool Call Start: The agent decided to invoke a tool. -- Tool Call Arguments: Arguments being passed to the tool. -- Tool Call End: Tool execution completed. -- Tool Call Result: Tool’s output is available. -- Text Message Start: The agent started composing a reply. -- Text Message Content: Streaming content chunks for progressive rendering. -- Text Message End: The agent reply is complete. -- Run Finished: The run is finalized; persisted messages will follow. - -### Agentic Messages - -These events are received via the **`MessageListener`** after the run completes. -- `AIAssistantMessage`: The full assistant reply. -- `AIToolResultMessage`: The final output of a tool call. -- `AIToolArgumentMessage`: The arguments that were passed to a tool. - - - - ```dart - const listenerId = "unique_listener_id"; - - class Class_Name with MessageListener { - // Adding the MessageListener - // CometChat.addMessageListener(listenerId, this); - @override - void onAIAssistantMessageReceived(AIAssistantMessage aiAssistantMessage) { - debugPrint("AI Assistant Message Received: ${aiAssistantMessage.toString()}"); - } - - @override - void onAIToolResultReceived(AIToolResultMessage aiToolArgumentMessage) { - debugPrint("AI Assistant Message Received: ${aiToolArgumentMessage.toString()}"); - } - - @override - void onAIToolArgumentsReceived(AIToolArgumentMessage aiToolArgumentMessage) { - debugPrint("AI Assistant Message Received: ${aiToolArgumentMessage.toString()}"); - } - } - ``` - - \ No newline at end of file diff --git a/sdk/flutter/v5/ai-chatbots-overview.mdx b/sdk/flutter/v5/ai-chatbots-overview.mdx deleted file mode 100644 index 7a460169b..000000000 --- a/sdk/flutter/v5/ai-chatbots-overview.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Bots" -url: "/ai-chatbots/overview" ---- diff --git a/sdk/flutter/v5/ai-moderation.mdx b/sdk/flutter/v5/ai-moderation.mdx deleted file mode 100644 index e0414eb82..000000000 --- a/sdk/flutter/v5/ai-moderation.mdx +++ /dev/null @@ -1,175 +0,0 @@ ---- -title: "AI Moderation" ---- - -## Overview - -AI Moderation in the CometChat SDK helps ensure that your chat application remains safe and compliant by automatically reviewing messages for inappropriate content. This feature leverages AI to moderate messages in real-time, reducing manual intervention and improving user experience. - - -For a broader understanding of moderation features, configuring rules, and managing flagged messages, see the [Moderation Overview](/moderation/overview). - - -## Prerequisites - -Before using AI Moderation, ensure the following: - -1. Moderation is enabled for your app in the [CometChat Dashboard](https://app.cometchat.com) -2. Moderation rules are configured under **Moderation > Rules** -3. You're using CometChat SDK version that supports moderation - -## How It Works - -```mermaid -sequenceDiagram - participant App - participant SDK - participant CometChat - participant Moderation - - App->>SDK: sendMessage() - SDK->>CometChat: Send Message - CometChat->>Moderation: Process Message - CometChat-->>SDK: Message (status: PENDING) - SDK-->>App: onSuccess (PENDING) - Moderation-->>CometChat: Moderation Result - CometChat-->>SDK: onMessageModerated - SDK-->>App: APPROVED or DISAPPROVED -``` - -| Step | Description | -|------|-------------| -| 1. Send Message | App sends a text, image, or video message | -| 2. Pending Status | Message is sent with `PENDING` moderation status | -| 3. AI Processing | Moderation service analyzes the content | -| 4. Result Event | `onMessageModerated` event fires with final status | - -## Supported Message Types - -Moderation is triggered **only** for the following message types: - -| Message Type | Moderated | Notes | -|--------------|-----------|-------| -| Text Messages | ✅ | Content analyzed for inappropriate text | -| Image Messages | ✅ | Images scanned for unsafe content | -| Video Messages | ✅ | Videos analyzed for prohibited content | -| Custom Messages | ❌ | Not subject to AI moderation | -| Action Messages | ❌ | Not subject to AI moderation | - -## Moderation Status - -The `moderationStatus` property returns one of the following enum values: - -| Status | Enum Value | Description | -|--------|------------|-------------| -| Pending | `ModerationStatusEnum.PENDING` | Message is being processed by moderation | -| Approved | `ModerationStatusEnum.APPROVED` | Message passed moderation and is visible | -| Disapproved | `ModerationStatusEnum.DISAPPROVED` | Message violated rules and was blocked | - -## Implementation - -### Step 1: Send a Message and Check Initial Status - -When you send a text, image, or video message, check the initial moderation status: - - - - ```dart - TextMessage textMessage = TextMessage( - text: "Hello, how are you?", - receiverUid: receiverUID, - receiverType: ReceiverTypeConstants.user, - ); - - CometChat.sendMessage( - textMessage, - onSuccess: (TextMessage message) { - // Check moderation status - if (message.moderationStatus?.value == ModerationStatusEnum.PENDING.value) { - print("Message is under moderation review"); - // Show pending indicator in UI - } - }, - onError: (CometChatException e) { - print("Message sending failed: ${e.message}"); - }, - ); - ``` - - - -### Step 2: Listen for Moderation Results - -Implement the `MessageListener` to receive moderation results in real-time: - - - - ```dart - class ModerationListener with MessageListener { - - @override - void onMessageModerated(BaseMessage message) { - if (message is TextMessage) { - switch (message.moderationStatus?.value) { - case ModerationStatusEnum.APPROVED: - print("Message ${message.id} approved"); - // Update UI to show message normally - break; - - case ModerationStatusEnum.DISAPPROVED: - print("Message ${message.id} blocked"); - // Handle blocked message (hide or show warning) - handleDisapprovedMessage(message); - break; - } - } else if (message is MediaMessage) { - switch (message.moderationStatus?.value) { - case ModerationStatusEnum.APPROVED: - print("Media message ${message.id} approved"); - break; - - case ModerationStatusEnum.DISAPPROVED: - print("Media message ${message.id} blocked"); - handleDisapprovedMessage(message); - break; - } - } - } - } - - // Register the listener - CometChat.addMessageListener("MODERATION_LISTENER", ModerationListener()); - - // Don't forget to remove the listener when done - // CometChat.removeMessageListener("MODERATION_LISTENER"); - ``` - - - -### Step 3: Handle Disapproved Messages - -When a message is disapproved, handle it appropriately in your UI: - - - - ```dart - void handleDisapprovedMessage(BaseMessage message) { - int messageId = message.id; - - // Option 1: Hide the message completely - hideMessageFromUI(messageId); - - // Option 2: Show a placeholder message - showBlockedPlaceholder(messageId, "This message was blocked by moderation"); - - // Option 3: Notify the sender (if it's their message) - if (message.sender?.uid == currentUserUID) { - showNotification("Your message was blocked due to policy violation"); - } - } - ``` - - - -## Next Steps -After implementing AI Moderation, consider adding a reporting feature to allow users to flag messages they find inappropriate. For more details, see the [Flag Message](/sdk/flutter/flag-message) documentation. diff --git a/sdk/flutter/v5/ai-user-copilot-overview.mdx b/sdk/flutter/v5/ai-user-copilot-overview.mdx deleted file mode 100644 index 3e798a3fb..000000000 --- a/sdk/flutter/v5/ai-user-copilot-overview.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "AI" -url: "/fundamentals/ai-user-copilot/overview" ---- \ No newline at end of file diff --git a/sdk/flutter/v5/authentication-overview.mdx b/sdk/flutter/v5/authentication-overview.mdx deleted file mode 100644 index 87b335748..000000000 --- a/sdk/flutter/v5/authentication-overview.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: "Authentication" -sidebarTitle: "Overview" ---- - -To allow a user to use CometChat, the user must log in to CometChat. - -**CometChat does not handle user management.** You must handle user registration and login at your end. Once the user is logged into your app/site, you can log in the user to CometChat **programmatically**. The user does not ever directly login to CometChat. - -**CometChat does not handle friends management.** If you want to associate friends with your users, you must handle friends management in your app. Once two users are friends, you can associate them as friends in CometChat. - -### Typical Workflow - -```mermaid -sequenceDiagram - participant App as Your App - participant Server as Your Server - participant CC as CometChat - - Note over App,CC: Registration - App->>Server: User registers - Server->>Server: Store user in DB - Server->>CC: Create user via REST API - - Note over App,CC: Login - App->>Server: User logs in - Server->>Server: Verify credentials - Server->>CC: Generate Auth Token - Server-->>App: Return Auth Token - App->>CC: CometChat.login(uid, authToken) - CC-->>App: Session established - - Note over App,CC: Messaging - App->>CC: Send and receive messages -``` - -| Your App | Your Server | CometChat | -|----------|-------------|-----------| -| User registers | Store user info in your database | Create user via REST API (ID & name) | -| User logs in | Verify credentials, retrieve user ID | Log in user programmatically | -| User sends a friend request | Display the request to the potential friend | No action required | -| User accepts a friend request | Display the users as friends | Add both users as friends via REST API | - -## Create User - -Before you login the user, you must add the user to CometChat. - -1. **For proof of concept/MVPs**: Create the user using the [CometChat Dashboard](https://app.cometchat.com/). -2. **For production apps**: Use the CometChat [Create User API](https://api-explorer.cometchat.com/reference/creates-user) to create the user when your user signs up in your app. - - - -We have setup 5 users for testing having UIDs: `cometchat-uid-1`, `cometchat-uid-2`, `cometchat-uid-3`, `cometchat-uid-4` and `cometchat-uid-5`. - - - -Once initialization is successful, you will need to log the user into CometChat using the `login()` method. - -We recommend you call the CometChat `login()` method once your user logs into your app. The `login()` method needs to be called only once. - - - -The CometChat SDK maintains the session of the logged-in user within the SDK. Thus you do not need to call the login method for every session. You can use the CometChat.getLoggedInUser() method to check if there is any existing session in the SDK. This method should return the details of the logged-in user. If this method returns null, it implies there is no session present within the SDK and you need to log the user into ComeChat. - - - -## Login using Auth Key - -This straightforward authentication method is ideal for proof-of-concept (POC) development or during the early stages of application development. For production environments, however, we strongly recommend using an [AuthToken](#login-using-auth-token) instead of an Auth Key to ensure enhanced security. - - - -```dart -String UID = "user_id"; // Replace with the UID of the user to login -String authKey = "AUTH_KEY"; // Replace with your App Auth Key - -final user = await CometChat.getLoggedInUser(); -if (user == null) { -await CometChat.login(UID, authKey, - onSuccess: (User user) { - debugPrint("Login Successful : $user" ); - }, onError: (CometChatException e) { - debugPrint("Login failed with exception: ${e.message}"); - }); -} -``` - - - - - -| Parameter | Description | -| --------- | -------------------------------------------------- | -| UID | The `UID` of the user that you would like to login | -| authKey | CometChat App Auth Key | - -After the user logs in, their information is returned in the `User` object. - -## Login using Auth Token - -This advanced authentication procedure does not use the Auth Key directly in your client code thus ensuring safety. - -1. [Create a User](https://api-explorer.cometchat.com/reference/creates-user) via the CometChat API when the user signs up in your app. -2. [Create an Auth Token](https://api-explorer.cometchat.com/reference/create-authtoken) via the CometChat API for the new user every time the user logs in to your app. -3. Pass the **Auth Token** to your client and use it in the `login()` method. - - - -```dart -String authToken = "AUTH_TOKEN"; -var user = await CometChat.getLoggedInUser(onSuccess: (_){}, onError: (_){}); -if (user == null) { - if(authToken!=null){ - await CometChat.loginWithAuthToken(authToken, - onSuccess: (User loggedInUser){ - debugPrint("Login Successful : $user" ); - }, onError: ( CometChatException e){ - debugPrint("Login failed with exception: ${e.message}"); - }); - } -} -``` - - - - - -| Parameter | Description | -| ---------------------------------------------- | ----------- | -| authToken | Auth Token of the user you would like to login | - -After the user logs in, their information is returned in the `User` object. - -## Logout - -You can use the `logout()` method to log out the user from CometChat. We suggest you call this method once your user has been successfully logged out from your app. - - - -```dart -CometChat.logout( onSuccess: ( successMessage) { - debugPrint("Logout successful with message $successMessage"); - }, onError: (CometChatException e ){ - debugPrint("Logout failed with exception: ${e.message}"); - } - ); -``` - - - - diff --git a/sdk/flutter/v5/block-users.mdx b/sdk/flutter/v5/block-users.mdx deleted file mode 100644 index 8613e6df1..000000000 --- a/sdk/flutter/v5/block-users.mdx +++ /dev/null @@ -1,140 +0,0 @@ ---- -title: "Block Users" ---- - - - -## Block Users - -*In other words, as a logged-in user, how do I block a user from sending me messages?* - -You can block users using the `blockUsers()` method. Once any user is blocked, all the communication to and from the respective user will be completely blocked. You can block multiple users in a single operation. The `blockUsers()` method takes a `List` as a parameter which holds the list of `UIDs` to be blocked. - - - -```dart -List uids = []; -uids.add("UID1"); -uids.add("UID2"); -uids.add("UID3"); -CometChat.blockUser(uids, onSuccess: (Map map) { -debugPrint("Blocked User Successfully $map "); -}, onError: (CometChatException e) { -debugPrint("Blocked User Unsuccessful ${e.message} "); -}); -``` - - - - - -In the `onSuccess()` callback, you receive a Map which contains `UIDs` as the keys and `success` or `fail` as the value based on if the block operation for the `UID` was successful or not. - -## Unblock Users - -*In other words, as a logged-in user, how do I unblock a user I previously blocked?* - -You can unblock the already blocked users using the `unblockUsers()` method. You can unblock multiple users in a single operation. The `unblockUsers()` method takes a `List` as a parameter which holds the list of `UIDs` to be unblocked. - - - -```dart -List uids = []; -uids.add("UID1"); -uids.add("UID2"); -uids.add("UID3"); - -CometChat.unblockUser(uids, onSuccess: (Map map) { - debugPrint("Blocked User Successfully $map "); - }, onError: (CometChatException e) { - debugPrint("Blocked User Unsuccessful ${e.message} "); - }); -``` - - - - - -In the `onSuccess()` callback, you receive a Map which contains `UIDs` as the keys and `success` or `fail` as the value based on if the unblock operation for the `UID` was successful or not. - -## Get list of blocked users - -*In other words, as a logged-in user, how do I get a list of all users I've blocked?* - -In order to fetch the list of blocked users, you can use the `BlockedUsersRequest` class. To use this class i.e to create an object of the `BlockedUsersRequest class`, you need to use the `BlockedUsersRequestBuilder` class. The `BlockedUsersRequestBuilder` class allows you to set the parameters based on which the blocked users are to be fetched. - -The `BlockedUsersRequestBuilder` class allows you to set the below parameters: - -### Set Limit - -This method sets the limit i.e. the number of blocked users that should be fetched in a single iteration. - - - -```dart -BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() - ..limit = 50 - ).build(); -``` - - - - - -### Set Search Keyword - -This method allows you to set the search string based on which the blocked users are to be fetched. - - - -```dart -BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() - ..limit = 50 - ..searchKeyword = "abc" - ).build(); -``` - - - - - -### Set Direction - -* `CometChatBlockedUsersDirection.`*directionBlockedByMe* - This will ensure that the list of blocked users only contains the users blocked by the logged in user. -* `CometChatBlockedUsersDirection.`*directionHasBlockedMe* - This will ensure that the list of blocked users only contains the users that have blocked the logged in user. -* `CometChatBlockedUsersDirection.`*directionBoth* - This will make sure the list of users includes both the above cases. This is the default value for the direction variable if it is not set. - - - -```dart -BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() - ..limit = 50 - .direction = CometChatBlockedUsersDirection.directionBlockedByMe - ).build(); -``` - - - - - -Finally, once all the parameters are set in the builder class, you need to call the `build()` method to get the object of the `BlockedUsersRequest` class. - -Once you have the object of the `BlockedUsersRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `User` objects containing n number of blocked users where N is the limit set in the builder class. - - - -```dart -BlockedUsersRequest blockedUsersRequest = (BlockedUsersRequestBuilder() - ..limit = 30 - ).build(); - -blockedUsersRequest.fetchNext(onSuccess: (List userList){ - debugPrint("Custom Message Sent Successfully : $userList "); - }, onError: (CometChatException e){ - debugPrint("Blocked User Request failed with exception: ${e.message}"); -}); -``` - - - - diff --git a/sdk/flutter/v5/connection-status.mdx b/sdk/flutter/v5/connection-status.mdx deleted file mode 100644 index a0877d610..000000000 --- a/sdk/flutter/v5/connection-status.mdx +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: "Connection Status" ---- - - - -CometChat SDK provides you with a mechanism to get real-time status of the connection to CometChat web-socket servers. To achieve this you need to use the `ConnectionListener` class provided by the CometChat SDK - -Connection Status provides you with the below 3 methods to get the status of the connection to CometChat web-socket servers: - -| Delegate Method | Information | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| onConnecting | This method is triggered when CometChat SDK is trying to establish a connection to the web-socket server. | -| onConnected | This method is called when CometChat SDK has successfully established a connection and now is connected. | -| onDisconnected | This method is called when the CometChat SDK gets disconnected due to any issue while maintaining the connection like network fluctuations, etc. | -| onFeatureThrottled | CometChat automatically toggles off certain features to prevent performance loss for end-users under various circumstances | -| onConnectionError | This method is called when the CometChat SDK gets error due to any issue while maintaining the connection like network fluctuations, etc. | - -Once the connection is broken, the disconnected callback is triggered, the SDK automatically tries to establish the connection again, thus going into the connecting state and triggering the `connecting` method. Once the attempt to connect is successful, the `connected` method is triggered thus letting the developer know that the connection is established and is active. - -In order to use the ConnectionListeners, you need to add the ConnectionListeners using the `addConnectionListener` method provided by the SDK. You can add multiple listeners as shown below. Just make sure you add listeners with unique IDs. - - - -```dart -class Class_Name with ConnectionListener { -//1. Register Connection listener -//CometChat.addConnctionListener("listenerId", this); - -//2. Ovveride the ConnectionListener methods -@override -void onConnected() { - // TODO: implement onConnected -} - -@override -void onConnecting() { - // TODO: implement onConnecting -} - -@override -void onDisconnected() { - // TODO: implement onDisconnected -} - -@override -void onFeatureThrottled() { - // TODO: implement onFeatureThrottled -} - -@override -void onConnectionError(CometChatException error) { - // TODO: implement onFeatureThrottled -} - -} -``` - - - - - -You can also get the current connection status by using `getConnectionStatus` property provided by CometChat SDK - - - -```dart -String connectionStatus = CometChat.getConnectionStatus(); -``` - - - - - -The above method will return either of the below 3 values: - -1. `CometChatWSState.connected` (connected); -2. `CometChatWSState.connecting`(connecting) -3. `CometChatWSState.disconnected`(disconnected) -4. `CometChatWSState.featureThrottled`(featureThrottled) - - - -Know more about CometChat SDK connection behaviour [click here](/sdk/flutter/connection-behaviour) - - diff --git a/sdk/flutter/v5/create-group.mdx b/sdk/flutter/v5/create-group.mdx deleted file mode 100644 index feb4149fc..000000000 --- a/sdk/flutter/v5/create-group.mdx +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: "Create A Group" ---- - - - -## Create a Group - -*In other words, as a logged-in user, how do I create a public, private or password-protected group?* - -You can create a group using `createGroup()` method. This method takes a `Group` object as input. - -The `groupType` needs to be either of the below 3 values: - -1.`CometChatGroupType.`*public* (public) - -2.`CometChatGroupType.`*password* (password) - -3.`CometChatGroupType.`*private* (private) - - - -```dart -String GUID = "GUID"; - String groupName = "Hello Group!"; - String groupType = CometChatGroupType.public; - String password = ""; - - Group _group = Group(guid: GUID, name: groupName, type: groupType); - - - await CometChat.createGroup(group: _group, onSuccess: (Group group ){ - debugPrint("Group Created Successfully : $group "); - }, onError:(CometChatException e) { - debugPrint("Group Creation failed with exception: ${e.message}"); - } ); -``` - - - - - -The `createGroup()` method takes the following parameters: - -| Parameter | Description | -| --------- | ---------------------------- | -| `group` | An instance of `Group` class | - -After the successful creation of the group, you will receive an instance of `Group` class which contains all the information about the particular group. - - - -GUID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. - - - -## Create a Group with Members - -*In other words, how do I create a group and add members in a single step?* - -You can create a group and add members simultaneously using the `createGroupWithMembers()` method. This is more efficient than creating a group first and then adding members separately. - - - -```dart -String GUID = "GUID"; -String groupName = "Hello Group!"; -String groupType = CometChatGroupType.public; - -Group group = Group(guid: GUID, name: groupName, type: groupType); - -// Create a list of group members to add -List members = []; -members.add(GroupMember(uid: "cometchat-uid-1", scope: CometChatMemberScope.participant)); -members.add(GroupMember(uid: "cometchat-uid-2", scope: CometChatMemberScope.moderator)); -members.add(GroupMember(uid: "cometchat-uid-3", scope: CometChatMemberScope.admin)); - -await CometChat.createGroupWithMembers( - group: group, - members: members, - onSuccess: (Group group, Map failedMembers) { - debugPrint("Group Created Successfully : $group"); - if (failedMembers.isNotEmpty) { - debugPrint("Failed to add some members: $failedMembers"); - } - }, - onError: (CometChatException e) { - debugPrint("Group Creation failed with exception: ${e.message}"); - } -); -``` - - - - - -The `createGroupWithMembers()` method takes the following parameters: - -| Parameter | Description | -| --------- | ---------------------------------------------------------- | -| `group` | An instance of `Group` class | -| `members` | A list of `GroupMember` objects to be added to the group | - -The `onSuccess` callback returns: -- `group`: The created `Group` object -- `failedMembers`: A map containing UIDs of members that failed to be added along with the error reason - -The `GroupMember` class takes the following parameters: - -| Parameter | Description | -| --------- | ------------------------------------------------------------------------------------------------ | -| `uid` | The UID of the user to be added as a member | -| `scope` | The scope of the member: `CometChatMemberScope.participant`, `moderator`, or `admin` | - -## Group Class - -| Field | Editable | Information | -| ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------- | -| guid | Needs to be specified at group creation. Cannot be edited later | A unique identifier for a group | -| name | Yes | Name of the group | -| type | No | Type of the group: Can be 1. Public 2. Password 3. Private | -| password | No | Password for the group in case the group is of type password. | -| icon | Yes | An URL to group icon | -| description | Yes | Description about the group | -| owner | Yes | UID of the owner of the group. | -| metadata | Yes | Additional data for the group as JSON | -| createdAt | No | The unix timestamp of the time the group was created | -| updatedAt | No | The unix timestamp of the time the group was last updated | -| hasJoined | No | A boolean to determine if the logged in user is a member of the group. | -| joinedAt | No | The unix timestamp of the time the logged in user joined the group. | -| scope | Yes | Scope of the logged in user. Can be: 1. Admin 2. Moderator 3. Participant | -| membersCount | No | The number of members in the groups | -| tags | Yes | A list of tags to identify specific groups. | -| isBannedFromGroup | No | A boolean indicating whether the logged-in user is banned from the group. | diff --git a/sdk/flutter/v5/delete-conversation.mdx b/sdk/flutter/v5/delete-conversation.mdx deleted file mode 100644 index 57473b8ca..000000000 --- a/sdk/flutter/v5/delete-conversation.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: "Delete A Conversation" ---- - - - -In case you want to delete a conversation, you can use the `deleteConversation()` method. - -This method takes two parameters. The unique id (UID/GUID) of the conversation to be deleted & the type (user/group) of conversation to be deleted. - - - -```dart -String conversationWith = "UID"; -String conversationType = CometChatConversationType.user; -await CometChat.deleteConversation(conversationWith, conversationType, - onSuccess: (String str){ - debugPrint("Conversation deleted successfully at : $str"); - }, - onError: (CometChatException e){ - debugPrint("Conversation deletion failed : ${e.message}"); - } -); -``` - - - - -```dart -String conversationWith = "GUID"; -String conversationType = CometChatConversationType.group; -await CometChat.deleteConversation(conversationWith, conversationType, - onSuccess: (String str){ - debugPrint("Conversation deleted successfully at : $str"); - }, - onError: (CometChatException e){ - debugPrint("Conversation deletion failed : ${e.message}"); - } -); -``` - - - - - -This method deletes the conversation only for the logged-in user. To delete a conversation for all the users of the conversation, please refer to our REST API documentation [here](https://api-explorer.cometchat.com/reference/deletes-conversation). - -The `deleteConversation()` method takes the following parameters: - -| Parameter | Description | Required | -| ---------------- | --------------------------------------------------------------------------------- | -------- | -| conversationWith | `UID` of the user or `GUID` of the group whose conversation you want to delete. | YES | -| conversationType | The type of conversation you want to delete . It can be either `user` or `group`. | YES | diff --git a/sdk/flutter/v5/delete-group.mdx b/sdk/flutter/v5/delete-group.mdx deleted file mode 100644 index 2bc36f2e0..000000000 --- a/sdk/flutter/v5/delete-group.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: "Delete A Group" ---- - - - -## Delete Group - -To delete a group you need to use the `deleteGroup()` method. The user must be an **Admin** of the group they are trying to delete. - - - -```dart -String GUID = "GUID"; - -CometChat.deleteGroup(GUID, onSuccess: (String message) { -debugPrint("Deleted Group Successfully : $message "); -}, onError: (CometChatException e) { -debugPrint("Delete Group failed with exception: ${e.message}"); -}); -``` - - - - - -The `deleteGroup()` method takes the following parameters: - -| Parameter | Description | -| --------- | ---------------------------------------------- | -| `GUID` | The GUID of the group you would like to delete | diff --git a/sdk/flutter/v5/delete-message.mdx b/sdk/flutter/v5/delete-message.mdx deleted file mode 100644 index ebf63f117..000000000 --- a/sdk/flutter/v5/delete-message.mdx +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: "Delete A Message" ---- - - - -While [deleting a message](/sdk/flutter/delete-message#delete-a-message) is straightforward, receiving events for deleted messages with CometChat has two parts: - -1. Adding a listener to receive [real-time message deletes](/sdk/flutter/delete-message#real-time-message-delete-events) when your app is running. -2. Calling a method to retrieve [missed message delete events](/sdk/flutter/delete-message#missed-message-delete-events)-me when your app was not running. - -## Delete a Message - -*In other words, as a sender, how do I delete a message?* - -In case you have to delete a message, you can use the `deleteMessage()` method. This method takes the message ID of the message to be deleted. - - - -```dart -int messageId=1234; - -await CometChat.deleteMessage(messageId, - onSuccess: (BaseMessage message){ - debugPrint("Message deleted successfully at : $message"); - }, onError: (CometChatException e){ - debugPrint("Message deletion failed : ${e.message}"); - } -); -``` - - - - - -Once the message is deleted, In the `onSuccess()` callback, you get an object of the `BaseMessage` class, with the `deletedAt` field set with the timestamp of the time the message was deleted. Also, the `deletedBy` field is set. These two fields can be used to identify if the message is deleted while iterating through a list of messages. - -By default, CometChat allows certain roles to delete a message. - -## Real-time Message Delete Events - -*In other words, as a recipient, how do I know when someone deletes a message when my app is running?* - -| User Role | Conversation Type | Deletion Capabilities | -| --------------- | ----------------------- | ------------------------- | -| Message Sender | One-on-one Conversation | Messages they've sent | -| Message Sender | Group Conversation | Messages they've sent | -| Group Admin | Group Conversation | All messages in the group | -| Group Moderator | Group Conversation | All messages in the group | - -In order to receive real-time events for a message being deleted, you need to override the `onMessageDeleted()` method of the `MessageListener` class. - - - -```dart -class Class_Name with MessageListener { - -//CometChat.addMessageListener("listenerId", this); -@override -void onMessageDeleted(BaseMessage message) { - // TODO implement onMessageDeleted -} - - -} -``` - - - - - -## Missed Message Delete Events - -*In other words, as a recipient, how do I know if someone deleted a message when my app was not running?* - -When you retrieve the list of previous messages, for the messages that were deleted, the `deletedAt` and the `deletedBy` fields will be set. Also, for example, if the total number of messages for a conversation are 100, and the message with message ID 50 was deleted. Now the message with ID 50 will have the `deletedAt` and the `deletedBy` fields set whenever it is pulled from the history. Also, the 101st message will be an `Action` message informing you that the message with ID 50 has been deleted. - -For the message deleted event, in the `Action` object received, the following fields can help you get the relevant information- - -1. `action` - `deleted` -2. `actionOn` - Updated message object which was deleted. -3. `actionBy` - User object containing the details of the user who has deleted the message. -4. `actionFor` - User/group object having the details of the receiver to which the message was sent. - - - -In order to delete a message, you need to be either the sender of the message or the admin/moderator of the group in which the message was sent. - - diff --git a/sdk/flutter/v5/delivery-read-receipts.mdx b/sdk/flutter/v5/delivery-read-receipts.mdx deleted file mode 100644 index 19be057f3..000000000 --- a/sdk/flutter/v5/delivery-read-receipts.mdx +++ /dev/null @@ -1,280 +0,0 @@ ---- -title: "Delivery & Read Receipts" ---- - - - -## Mark Messages as Delivered - -*In other words, as a recipient, how do I inform the sender that I've received a message?* - -You can mark the messages for a particular conversation as read using the `markAsDelivered()` method. This method takes the below parameters as input: - -| Parameter | Information | -| ------------ | ---------------------------------------------------------------------------------------------------------- | -| id | The ID of the message above which all the messages for a particular conversation are to be marked as read. | -| receiverUid | In case of one to one conversation message's sender UID will be the receipt's receiver Id. | -| receiverType | Type of the receiver. Could be either of the two values( user or group). | - -Messages for both user & group conversations can be marked as read using this method. - -Ideally, you would like to mark all the messages as delivered for any conversation when the user opens the chat window for that conversation. This includes two scenarios: - -1. **When the list of messages for the conversation is fetched**: In this case you need to obtain the last message in the list of messages and pass the message ID of that message to the markAsDelivered() method. -2. **When the user is on the chat window and a real-time message is received:** In this case you need to obtain the message ID of the message and pass it to the markAsDelivered() method. - -This method will mark all the messages before the messageId specified, for the conversation with receiverId and receiverType(user/group) as read. - -In case you would like to be notified of an error if the receipts fail to go through you can use `markAsDelivered()` method with the callbacks as shown below: - - - -```dart -CometChat.markAsDelivered(message, onSuccess: (String unused) { -debugPrint("markAsDelivered : $unused "); -}, onError: (CometChatException e) { -debugPrint("markAsDelivered unsuccessful : ${e.message} "); -}); -``` - - - - - -## Mark Messages as Read - -*In other words, as a recipient, how do I inform the sender I've read a message?* - -You can mark the messages for a particular conversation as read using the `markAsRead()` method. This method takes the below parameters as input: - -Messages for both user and group conversations can be marked as read using this method. - -The message object takes the below parameters as input: - -| Parameter | Information | -| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| id | The ID of the message above which all the messages for a particular conversation are to be marked as read. | -| receiverUid | In a one-to-one conversation, the message's sender UID will be the receipt's receiver ID. In a group conversation, the message's receiver ID will be the receipt's receiver ID. | -| receiverType | Type of the receiver. Could be either of the two values (user or group). | - -Messages for both user and group conversations can be marked as read using this method. - -Ideally, you should mark all the messages as read for any conversation when the user opens the chat window for that conversation. This includes two scenarios: - -1. **When the list of messages for the conversation is fetched**: In this case you need to obtain the last message in the list of messages and pass the message to the `markAsRead()` method. -2. **When the user is on the chat window and a real-time message is received:** In this case you need to obtain the message and pass it to the `markAsRead()` method - -This method will mark all the messages before the message specified, for the conversation with `receiverId` and `receiverType` (user/group) as read. - -Another option the CometChat SDK provides is to pass the entire message object to the markAsRead() method. If the message object is the last message, the entire conversation will be marked as read. - - - -```dart -CometChat.markAsRead(message) -``` - - - - - -In case you would like to be notified of an error if the receipts fail to go through you can use the `markAsRead()` method with the callbacks as shown below: - - - -```dart -CometChat.markAsRead(message, onSuccess: (String unused) { - debugPrint("markAsRead : $unused "); - reinitiateList(); - }, onError: (CometChatException e) { - debugPrint("markAsRead unsuccessfull : ${e.message} "); - }); -``` - - - - - -## Mark Messages as Unread - -The Mark as Unread feature allows users to designate specific messages or conversations as unread, even if they have been previously viewed. - -This feature is valuable for users who want to revisit and respond to important messages or conversations later, ensuring they don't forget or overlook them. - -*In other words, how I can mark message as unread?* - -You can mark the messages for a particular conversation as unread using the `markMessageAsUnread()` method. - -| Parameter | Information | -| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| message | To mark a message as unread, pass a non-null `BaseMessage` instance to the `markMessageAsUnread()` function. All messages below that message in the conversation will contribute to the unread messages count. Example : When User B sends User A a total of 10 messages, and User A invokes the `markMessageAsUnread()` method on the fifth message, all messages located below the fifth message within the conversation list will be designated as unread. This results in a notification indicating there are 5 unread messages in the conversation list. | - - - -```dart -CometChat.markMessageAsUnread( - message, - onSuccess: (success) { - debugPrint("markMessageAsUnread : $success"); - }, - onError: (error) { - debugPrint("markMessageAsUnread unsuccessful : $error"); - }, - ); -``` - - - - - -## Mark Conversation as Delivered - -*In other words, how do I mark an entire conversation as delivered?* - -You can mark an entire conversation as delivered using the `markConversationAsDelivered()` method. This is useful when you want to signal that all messages in a conversation have been delivered to the recipient. - - - -```dart -CometChat.markConversationAsDelivered( - conversation, - onSuccess: (success) { - debugPrint("markConversationAsDelivered : $success"); - }, - onError: (error) { - debugPrint("markConversationAsDelivered unsuccessful : $error"); - }, - ); -``` - - - - - -## Mark Conversation as Read - -*In other words, how do I mark an entire conversation as read?* - -You can mark an entire conversation as read using the `markConversationAsRead()` method. This updates the status of all messages in the conversation to "read". - - - -```dart -CometChat.markConversationAsRead( - conversation, - onSuccess: (success) { - debugPrint("markConversationAsRead : $success"); - }, - onError: (error) { - debugPrint("markConversationAsRead unsuccessful : $error"); - }, - ); -``` - - - - - -## Receive Delivery & Read Receipts - -*In other words, as a recipient, how do I know when a message I sent has been delivered or read by someone?* - -### Real-time events - -1. `onMessagesDelivered()` - This event is triggered when a message is delivered to a user. -2. `onMessagesRead()` - This event is triggered when a message is read by a user. -3. `onMessagesDeliveredToAll()` - This event is triggered when a group message is delivered to all members of the group. This event is only for Group conversations. -4. `onMessagesReadByAll()` - This event is triggered when a group message is read by all members of the group. This event is only for Group conversations. - - - -```dart -class Class_Name with MessageListener { - -//CometChat.addMessageListener("listenerId", this); -@override -void onMessagesDelivered(MessageReceipt messageReceipt) { - // TODO: implement onMessagesDelivered -} - -@override -void onMessagesRead(MessageReceipt messageReceipt) { - // TODO: implement onMessagesRead -} - -@override -void onMessagesDeliveredToAll(MessageReceipt messageReceipt) { - // TODO: implement onMessagesDeliveredToAll -} - -@override -void onMessagesReadByAll(MessageReceipt messageReceipt) { - // TODO: implement onMessagesReadByAll -} - -} -``` - - - - - -You will receive events in the form of `MessageReceipt` objects. The message receipt contains the following parameters: - -| Parameter | Information | -| -------------- | -------------------------------------------------------------------------------------------------------------------- | -| `messageId` | The ID of the message prior to which all the messages for that particular conversation have been marked as read. | -| `sender` | User object containing the details of the user who has marked the message as read. | -| `receiverId` | Id of the receiver whose conversation has been marked as read. | -| `receiverType` | Type of the receiver (user/group) | -| `receiptType` | Type of the receipt (read/delivered) | -| `deliveredAt` | The timestamp of the time when the message was delivered. This will only be present if the receiptType is delivered. | -| `readAt` | The timestamp of the time when the message was read. This will only be present when the receiptType is read. | - -### Missed Receipts - -You will receive message receipts when you load offline messages. While fetching messages in bulk, the message object will have two fields i.e. `deliveredAt` and `readAt` which hold the timestamp for the time the message was delivered and read respectively. Using these two variables, the delivery and read status for a message can be obtained. - -However, for a group message, if you wish to fetch the `deliveredAt` and `readAt` fields of individual member of the group you can use the below-described method. - -### Receipt History for a Single Message - -In order to fetch the message receipts, you can use the `getMessageReceipts()` method. - - - -```dart -private int messageId = 10101; - -CometChat.getMessageReceipts(messageId, new CometChat.CallbackListener>() { -@Override - public void onSuccess(List messageReceipts) { - // Handle message receipts -} - -@Override - public void onError(CometChatException e) { - // Handle error -} -}); -``` - - - - - -You will receive a list of `MessageReceipt` objects in the `onSuccess()` method. - - - -The following features will be available only if the **Enhanced Messaging Status** feature is enabled for your app. - -* `onMessagesDeliveredToAll` event, -* `onMessagesReadByAll` event, -* `deliveredAt` field in a group message, -* `readAt` field in a group message. -* `markMessageAsUnread` method. -* `markConversationAsDelivered` method. -* `markConversationAsRead` method. - - diff --git a/sdk/flutter/v5/edit-message.mdx b/sdk/flutter/v5/edit-message.mdx deleted file mode 100644 index 76ae225c6..000000000 --- a/sdk/flutter/v5/edit-message.mdx +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: "Edit A Message" ---- - - - -While editing a message is straightforward, receiving events for edited messages with CometChat has two parts: - -1. Adding a listener to receive [real-time message edit events](/sdk/flutter/edit-message#real-time-message-edit-events) when your app is running -2. Calling a method to retrieve [missed message edit events](/sdk/flutter/edit-message#missed-message-edit-events) when your app was not running - -## Edit a Message - -*In other words, as a sender, how do I edit a message?* - -In order to edit a message, you can use the `editMessage()` method. This method takes an object of the `BaseMessage` class. At the moment, you are only allowed to edit `TextMessage` and `CustomMessage`. Thus, the `BaseMessage` object must either be a Text or a Custom Message. - -### Add/Update Tags - -While editing a message, you can update the tags associated with the Message. You can use the `setTags()` method to do so. The tags added while editing a message will replace the tags set when the message was sent. - - - -```dart -List tags = []; -tags.add("pinned"); -textMessage.tags = tags; -``` - - - - - -Once the message object is ready, you can use the `editMessage()` method and pass the message object to it. - - - -```dart -TextMessage updatedMessage = TextMessage( - text: updatedText, - receiverUid: receiverID, - receiverType: receiverType, - type: type); - -updatedMessage.id = message.id; - -await CometChat.editMessage(updatedMessage, - onSuccess: (BaseMessage message){ - debugPrint("Message Edited successfully: $message"); - }, onError: (CometChatException e){ - debugPrint("Message Edited failed with exception: ${e.message}"); - } -); -``` - - - - - -The object of the edited message will be returned in the `onSuccess()`callback method of the listener. The message object will contain the `editedAt` field set with the timestamp of the time the message was edited. This will help you identify if the message was edited while iterating through the list of messages. The `editedBy` field is also set to the `UID` of the user who edited the message. - -By default, CometChat allows certain roles to edit a message. - -| User Role | Conversation Type | Edit Capabilities | -| --------------- | ----------------------- | ------------------------- | -| Message Sender | One-on-one Conversation | Messages they've sent | -| Message Sender | Group Conversation | Messages they've sent | -| Group Owner | Group Conversation | All messages in the group | -| Group Moderator | Group Conversation | All messages in the group | - -## Real-time Message Edit Events - -*In other words, as a recipient, how do I know when someone has edited their message when my app is running?* - -In order to receive real-time events for message being edited, you need to override the `onMessageEdited()` method of the `MessageListener` class. - - - -```dart -class Class_Name with MessageListener { - -//CometChat.addMessageListener("listenerId", this); -@override -void onMessageEdited(BaseMessage message) { - // TODO: implement onMessageEdited -} - - -} -``` - - - - -## Missed Message Edit Events - -*In other words, as a recipient, how do I know when someone edited their message when my app was not running?* - -When you retrieve the list of previous messages, for the message that was edited, the `editedAt` and the `editedBy` fields will be set. Also, for example, if the total number of messages for a conversation is 100, and the message with message ID 50 was edited. Now the message with ID 50 will have the `editedAt` and the `editedBy` fields set whenever it is pulled from the history. Also, the 101st message will be an `Action` message informing you that the message with ID 50 has been edited.. - -For the message edited event, in the `Action` object received, the following fields can help you get the relevant information- - -1. `action` - `edited` -2. `actionOn` - Updated message object with the edited details. -3. `actionBy` - User object containing the details of the user who has edited the message. -4. `actionFor` - User/group object having the details of the receiver to which the message was sent. - - - -In order to edit a message, you need to be either the sender of the message or the admin/moderator of the group in which the message was sent. - - diff --git a/sdk/flutter/v5/extensions-overview.mdx b/sdk/flutter/v5/extensions-overview.mdx deleted file mode 100644 index 29c1774bd..000000000 --- a/sdk/flutter/v5/extensions-overview.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Extensions" -url: "/fundamentals/extensions-overview" ---- \ No newline at end of file diff --git a/sdk/flutter/v5/group-add-members.mdx b/sdk/flutter/v5/group-add-members.mdx deleted file mode 100644 index e125be98b..000000000 --- a/sdk/flutter/v5/group-add-members.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: "Add Members To A Group" ---- - - - -## Add Members to Group - -You can add members to the group using the `addMembersToGroup()` method. This method takes the below parameters: - -1. `GUID` - GUID of the group users are to be added to. -2. `List members` - This is a list of `GroupMember` objects. In order to add members, you need to create an object of the `GroupMember` class. The `UID` and the scope of the `GroupMember` are mandatory. -3. `List bannedMembers` - This is the list of `UIDs` that need to be banned from the group. This can be set to `null` if there are no members to be banned. - - - -```dart -List groupMembers = []; -GroupMember firstMember = GroupMember.fromUid( -scope: CometChatMemberScope.participant, -uid: "cometchat-uid-3",name: "name"); -GroupMember secondMember = GroupMember.fromUid(scope: CometChatMemberScope.admin, -uid: "cometchat-uid-4",name: "name"); - -groupMembers = [firstMember, secondMember]; - -CometChat.addMembersToGroup( guid: conversationWith, - groupMembers: groupMembers, - onSuccess: (Map result) { - debugPrint("Group Member added Successfully : $result"); - }, - onError: (CometChatException e) { - debugPrint("Group Member addition failed with exception: ${e.message}"); - }); -``` - - - - - -In the `onSuccess()` method , you will receive a Map which will contain the `UID` of the users and the value will either be `success` or an error message describing why the operation to add the user to the group or ban the user failed. - -## Real-Time Group Member Added Events - -*In other words, as a member of a group, how do I know when someone is added to the group when my app is running?* - - - -When a group member is added by another member, this event is triggered. When a user joins a group on their own, the joined event is triggered. - - - -To receive real-time events whenever a new member is added to a group, you need to implement the `onMemberAddedToGroup()` methods of the `GroupListener` class. - -* `onMemberAddedToGroup()` - This method is triggered when any user is added to the group so that the logged-in user is informed of the other members added to the group. - - - -```dart -class Class_Name with GroupListener { - -//CometChat.addGroupListener("group_Listener_id", this); -@override -void onMemberAddedToGroup(Action action, User addedby, User userAdded, Group addedTo) { - print("onMemberAddedToGroup"); -} -} -``` - - - - - -## Member Added to Group event in Message History - -*In other words, as a member of a group, how do I know when someone is added to the group when my app is not running?* - -When you retrieve the list of previous messages if a member has been added to any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. - -For the group member added event, in the `Action` object received, the following fields can help you get the relevant information- - -1. `action` - `added` -2. `actionOn` - User object containing the details of the user who was added to the group -3. `actionBy` - User object containing the details of the user who added the member to the group -4. `actionFor` - Group object containing the details of the group to which the member was added diff --git a/sdk/flutter/v5/group-change-member-scope.mdx b/sdk/flutter/v5/group-change-member-scope.mdx deleted file mode 100644 index 412b54b93..000000000 --- a/sdk/flutter/v5/group-change-member-scope.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: "Change Member Scope" ---- - - - -## Change Scope of a Group Member - -In order to change the scope of a group member, you can use the `changeGroupMemberScope()`. - - - -```dart -String UID = "UID"; -String GUID = "GUID"; -String scope = CometChatMemberScope.admin; - -CometChat.updateGroupMemberScope(guid: GUID, uid: UID,scope: scope, - onSuccess: (String message) { - debugPrint("Group Member Scope Changed Successfully : $message"); - }, - onError: (CometChatException e) { - debugPrint("Group Member Scope Change failed : ${e.message}"); - }); -``` - - - - - -This method takes the below parameters: - -| Parameter | Description | -| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `UID` | The UID of the member whose scope you would like to change | -| `GUID` | The GUID of the group for which the member's scope needs to be changed | -| `scope` | the updated scope of the member. This can be either of the 3 values: 1.`CometChatMemberScope.`*admin* (admin) 2.`CometChatMemberScope.`*moderator* (moderator) 3.`CometChatMemberScope.`*participant* (participant) | - -The default scope of any member is `participant`. Only the **Admin** of the group can change the scope of any participant in the group. - -## Real-Time Group Member Scope Changed Events - -*In other words, as a member of a group, how do I know when someone's scope is changed when my app is running?* - -In order to receive real-time events whenever a group member's scope changes, you will need to override the `onGroupMemberScopeChanged()` method of the `GroupListener` class - - - -```dart -class Class_Name with GroupListener { - -//CometChat.addGroupListener("group_Listener_id", this); -@override -void onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group) { - print("onGroupMemberScopeChanged "); - -} -} -``` - - - - - -## Missed Group Member Scope Changed Events - -*In other words, as a member of a group, how do I know when someone's scope is changed when my app is not running?* - -When you retrieve the list of previous messages if a member's scope has been changed for any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. - -For the group member scope changed event, in the `Action` object received, the following fields can help you get the relevant information- - -1. `action` - `scopeChanged` -2. `actionOn` - User object containing the details of the user whos scope has been changed -3. `actionBy` - User object containing the details of the user who changed the scope of the member -4. `actionFor` - Group object containing the details of the group in which the member scope was changed -5. `oldScope` - The original scope of the member -6. `newScope` - The updated scope of the member diff --git a/sdk/flutter/v5/groups-overview.mdx b/sdk/flutter/v5/groups-overview.mdx deleted file mode 100644 index f219e6c57..000000000 --- a/sdk/flutter/v5/groups-overview.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: "Groups" -sidebarTitle: "Overview" ---- - -A group can be used for multiple users to communicate with each other on a particular topic or interest. - -## GUID - -- Each group is uniquely identified using a GUID. -- The GUID is typically the primary ID of the group from your database. If you do not store group information in your database, you can generate a random string for use as GUID. - - -GUID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. - - -## Group Types - -CometChat supports three different types of groups: - -```mermaid -flowchart LR - G["Group"] --> Public - G --> Password - G --> Private - - Public -.- P1["Visible to all, anyone can join"] - Password -.- P2["Visible to all, requires password"] - Private -.- P3["Hidden, invite only"] -``` - -| Type | Visibility | Participation | -|------|-----------|---------------| -| Public | All users | Any user can choose to join | -| Password | All users | Any user with a valid password can join | -| Private | Only members | Invited users are auto-joined | - -## Member Scopes - -Once a participant joins a group, they become a member. Members remain part of the group indefinitely — to stop receiving messages, calls, and notifications, a member must be kicked, banned, or leave the group. - -```mermaid -flowchart TD - Admin["Admin (Group creator)"] - Moderator["Moderator"] - Participant["Participant (Default scope)"] - - Admin -->|Can promote or demote| Moderator - Admin -->|Can promote or demote| Participant - Moderator -->|Can promote or demote| Participant - - Admin -.- A1["Full control: manage members, update/delete group, message & call"] - Moderator -.- M1["Moderate: kick/ban participants, update group, message & call"] - Participant -.- P1["Basic: send & receive messages & calls"] -``` - -| Scope | Default | Privileges | -|-------|---------|-----------| -| Admin | Group creator | Manage all members, add members, kick & ban anyone, send & receive messages & calls, update & delete group | -| Moderator | — | Change scope of participants, kick & ban participants, update group, send & receive messages & calls | -| Participant | All other members | Send & receive messages & calls | diff --git a/sdk/flutter/v5/interactive-messages.mdx b/sdk/flutter/v5/interactive-messages.mdx deleted file mode 100644 index ce85974c3..000000000 --- a/sdk/flutter/v5/interactive-messages.mdx +++ /dev/null @@ -1,248 +0,0 @@ ---- -title: "Interactive Messages" ---- - - - -An `InteractiveMessage` is a specialized object that encapsulates an interactive unit within a chat message, such as an embedded form that users can fill out directly within the chat interface. This enhances user engagement by making the chat experience more interactive and responsive to user input. - -`InteractiveMessage` is a chat message with embedded interactive content. It can contain various properties: - -| Parameter | Description | -| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| receiverId | The UID or GUID of the recipient | -| receiverType | The type of the receiver to whom the message is to be sent i.e `CometChatConstants.RECEIVER_TYPE_USER` (user) or `CometChatConstants.RECEIVER_TYPE_GROUP` (group) | -| messageType | The type of the message that needs to be sent | -| interactiveData | A JSONObject holding structured data for the interactive element | -| allowSenderInteraction | A boolean determining whether the message sender can interact with the message, by default, it is set to false | -| interactionGoal | An InteractionGoal object encapsulating the intended outcome of interacting with the InteractiveMessage, by default, it is set to none | - -### Interaction - -An `Interaction` represents a user action involved with an `InteractiveMessage`. It includes: - -* `elementId`: An identifier for a specific interactive element. -* `interactedAt`: A timestamp indicating when the interaction occurred. - -### Mark as Interacted - -This method marks a message as interacted by identifying it with the provided Id. it also logs the interactive element associated with the interaction. - - - -```dart -await CometChat.markAsInteracted(messageId, interactedElementId, - onSuccess: (String message ){ - //write code here for after success - - }, onError: - (CometChatException excep){ - //write code here for after err - - }); -``` - - - - - -### Goal Completion - -A key feature of `InteractiveMessage` is checking whether a user's interactions with the message meet the defined `InteractionGoal` - -| | | | -| -------------------------------- | ---------------------------------------------------------------------- | ----------------------------- | -| **Any Interaction** | The goal is considered completed if there is at least one interaction. | InteractionGoalType.anyAction | -| **Any of Specific Interactions** | The goal is achieved if any of the specified interactions occurred. | InteractionGoalType.anyOf | -| **All of Specific Interactions** | The goal is completed when all specified interactions occur. | InteractionGoalType.allOf | -| **None** | The goal is never completed | InteractionGoalType.none | - -You would be tracking every interaction users perform on an `InteractiveMessage` (captured as `Interaction` objects) and comparing those with the defined `InteractionGoal`. The completion of a goal can vary depending on the goal type: - -This user interaction tracking mechanism provides a flexible and efficient way to monitor user engagement within an interactive chat session. By defining clear interaction goals and checking user interactions against these goals, you can manage user engagement and improve the overall chat experience in your CometChat-enabled application. - -### InteractionGoal - -The `InteractionGoal` represents the desired outcome of an interaction with an InteractiveMessage. It includes: - -* `elementIds`: A list of identifiers for the interactive elements. -* `type`: The type of interaction goal from the `CometChatConstants`. - -### Sending InteractiveMessages - -The InteractiveMessage can be sent using the sendInteractiveMessage method of the CometChat class. The method requires an InteractiveMessage object and a CallbackListener for handling the response. - -Here is an example of how to use it: - - - -```dart -final Map interactiveData = { - "title": "Survey", - "formFields": [ - { - "elementType": "textInput" , - "elementId": "name", - "optional": false, - "label": "Name", - "placeholder": { - "text": "Enter text here" - } - }, - { - "elementType": "textInput", - "elementId": "age", - "optional": true, - "label": "Age", - "maxLines": 1, - "placeholder": { - "text": "Enter text here" - } - }, - { - "elementType": "Select", - "elementId": "checkBox1", - "optional": true, - "label": "Check box element", - "defaultValue":["chk_option_2"], - "options" : [ - { - "label": "Option 1", - "value": "chk_option_1", - }, - { - "label": "Option 2", - "value": "chk_option_2", - } - - ] - }, - { - "elementType": "dropdown", - "elementId": "gender", - "optional": false, - "label": "Gender", - "defaultValue":"male", - "options" : [ - { - "label": "Male", - "value": "male", - }, - { - "label": "Female", - "value": "female", - } - - ] - }, - ], - "submitElement": { - "elementType": "button", - "elementId": "submitButton", - "buttonText": "Submit", - "disableAfterInteracted": false, - "action": { - "actionType": "urlNavigation", - "url": "https://www.cometchat.com/", - } - }, -}; - - - -InteractiveMessage interactiveMessage = -InteractiveMessage(interactiveData: interactiveData, - receiverUid: "cometchat-uid-3", //Replace this with receiver id - type: "form", - receiverType: "user",//replace this with receiver type - allowSenderInteraction: true, - muid: DateTime.now().millisecondsSinceEpoch.toString(), - interactionGoal: InteractionGoal( - type: InteractionGoalType.none - ) - ); - - -CometChat.sendInteractiveMessage(interactiveMessage, onSuccess: (InteractiveMessage message){ - -print(interactiveMessage); - -}, onError: (CometChatException excep){ -print(excep); -}); -``` - - - - - -### Event Listeners - -CometChat SDK provides event listeners to handle real-time events related to `InteractiveMessage`. - -### On InteractiveMessage Received - -The `onInteractiveMessageReceived` event listener is triggered when an `InteractiveMessage` is received. - -Here is an example: - - - -```dart -class DemoClass with MessageListener{ - -String listenerId = "UNIQUE_LISTENER_ID"; //Replace with unique listener id - - -CometChat.addMessageListener(_listenerId, this); - - - - @override -void onInteractiveMessageReceived(InteractiveMessage message){ - - //TODO implement onInteractiveMessageReceived event -} - -} -``` - - - - - -### On Interaction Goal Completed - -The `onInteractionGoalCompleted` event listener is invoked when an interaction goal is achieved. - -Here is an exampl - - - -```dart -class DemoClass with MessageListener{ - -String listenerId = "UNIQUE_LISTENER_ID"; //Replace with unique listener id - - -CometChat.addMessageListener(_listenerId, this); - - - -@override -void onInteractionGoalCompleted(InteractionReceipt receipt) { - //TODO implement onInteractionGoalCompleted -} - -} -``` - - - - - -These event listeners offer your application a way to provide real-time updates in response to incoming interactive messages and goal completions, contributing to a more dynamic and responsive user chat experience. - -### Usage - -An `InteractiveMessage` is constructed with the receiver's UID, the receiver type, the interactive type, and interactive data as a JSONObject. Once created, the `InteractiveMessage` can be sent using CometChat's `sendInteractiveMessage()` method. Incoming `InteractiveMessages` can be received and processed via CometChat's message listener framework. diff --git a/sdk/flutter/v5/join-group.mdx b/sdk/flutter/v5/join-group.mdx deleted file mode 100644 index 41e6b00da..000000000 --- a/sdk/flutter/v5/join-group.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: "Join A Group" ---- - - - -## Join a Group - -In order to start participating in group conversations, you will have to join a group. You can do so using the `joinGroup()` method. - - - -```dart -String GUID = "GUID"; -String groupType = CometChatGroupType.public; -String password = ""; - -CometChat.joinGroup(GUID, groupType, password: password, - onSuccess: (Group group) { - debugPrint("Group Joined Successfully : $group "); - }, onError: (CometChatException e) { - debugPrint("Group Joining failed with exception: ${e.message}"); - }); -``` - - - - - -The `joinGroup()` method takes the below parameters - -| Parameter | Description | -| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `GUID` | The GUID of the group you would like to join | -| `groupType` | Type of the group. CometChat provides 3 types of groups viz. 1.CometChatGroupType.*public* (public). 2.CometChatGroupType.*password* (password) 3.CometChatGroupType.*private* (private) | -| `password` | Password is mandatory in case of a password protected group. | - -Once you have joined a group successfully, you can send and receive messages in that group. - -CometChat keeps a track of the groups joined and you do not need to join the group everytime you want to communicate in the group. - -You can identify if a group is joined using the `hasJoined` parameter in the `Group` object. - -## Real-time Group Member Joined Events - -*In other words, as a member of a group, how do I know if someone joins the group when my app is running?* - -If a user joins any group, the members of the group receive a real-time event in the `onGroupMemberJoined()` method of the `GroupListener` class. - - - -```dart -class Class_Name with GroupListener { - -//CometChat.addGroupListener("group_Listener_id", this); -@override -void onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup) { - debugPrint("onGroupMemberJoined"); -} -``` - - - - - -## Missed Group Member Joined Events - -*In other words, as a member of a group, how do I know if someone joins the group when my app is not running?* - -When you retrieve the list of previous messages if a member has joined any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. - -For the group member joined event, in the `Action` object received, the following fields can help you get the relevant information- - -1. `action` - `joined` -2. `actionBy` - User object containing the details of the user who joined the group -3. `actionFor`- Group object containing the details of the group the user has joined diff --git a/sdk/flutter/v5/leave-group.mdx b/sdk/flutter/v5/leave-group.mdx deleted file mode 100644 index f73638b8d..000000000 --- a/sdk/flutter/v5/leave-group.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: "Leave A Group" ---- - - - -## Leave a Group - -In order to stop receiving updates and messages for any particular joined group, you will have to leave the group using the `leaveGroup()` method. - - - -```dart -String GUID = "GUID"; - -CometChat.leaveGroup(GUID, onSuccess: ( String message) { - debugPrint("Group Left Successfully : $message"); - },onError: (CometChatException e) { - debugPrint("Group Left failed : ${e.message}"); - }); -``` - - - - - -| Parameter | Description | -| --------- | --------------------------------------------- | -| `GUID` | The GUID of the group you would like to leave | - -Once a group is left, the user will no longer receive any updates or messages pertaining to the group. - -## Real-time Group Member Left Events - -*In other words, as a member of a group, how do I know if someone has left it when my app is running?* - -If a user leaves a group, the members of the group receive a real-time event in the `onGroupMemberLeft()` method of the `GroupListener` class. - - - -```dart -class Class_Name with GroupListener { - -//CometChat.addGroupListener("group_Listener_id", this); -@override -void onGroupMemberLeft(Action action, User leftUser, Group leftGroup) { - print("onGroupMemberLeft "); - -} -} -``` - - - - - -## Missed Group Member Left Events - -*In other words, as a member of a group, how do I know if someone has left it when my app is not running?* - -When you retrieve the list of previous messages if a member has left any group that the logged-in user is a member of, the list of messages will contain an `Action` message. An `Action` message is a sub-class of `BaseMessage` class. - -For the group member left event, in the `Action` object received, the following fields can help you get the relevant information- - -1. `action` - `left` -2. `actionBy` - User object containing the details of the user who left the group -3. `actionFor` - Group object containing the details of the group the user has left diff --git a/sdk/flutter/v5/mentions.mdx b/sdk/flutter/v5/mentions.mdx deleted file mode 100644 index 2a0ad288e..000000000 --- a/sdk/flutter/v5/mentions.mdx +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: "Mentions" ---- - - - -Mentions are a powerful tool for enhancing communication in messaging platforms. They streamline interaction by allowing users to easily engage and collaborate with particular individuals, especially in group conversations. - -Mentions in messages enable users to refer to specific individuals within a conversation. - -## Send Mentioned Messages - -Every User object has a String unique identifier associated with them which can be found in a property called uid. To mention a user in a message, the message text should contain the uid in following format: `<@uid:UID_OF_THE_USER>`. For example, to mention the user with UID cometchat-uid-1 in a text message, your text should be "`<@uid:cometchat-uid-1>`" - - - -```dart -String receiverID = "UID"; -User sender = User(name: "Sender", uid: "senderUID"); -String messageText = "Hello, <@uid:cometchat-uid-1>"; -String receiverType = CometChatReceiverType.user; - -TextMessage textMessage = TextMessage( - text: messageText, - sender: sender, - receiverUid: receiverID, - receiverType: receiverType, - category: CometChatMessageCategory.message, - type: CometChatMessageType.text); - -CometChat.sendMessage(textMessage, onSuccess:(TextMessage textMessage) { - print("Message sent successfully: ${textMessage.text}, mentioned users: ${textMessage.mentionedUsers}"); - }, - onError:(CometChatException e) { - print("Message sending failed with exception: $e"); - } -); -``` - - - - -```dart -String receiverID = "GUID"; -User sender = User(name: "Sender", uid: "senderUID"); -String messageText = "Hello, <@uid:cometchat-uid-1>"; -String receiverType = CometChatReceiverType.group; - -TextMessage textMessage = TextMessage( - text: messageText, - sender: sender, - receiverUid: receiverID, - receiverType: receiverType, - category: CometChatMessageCategory.message, - type: CometChatMessageType.text); - -CometChat.sendMessage(textMessage, onSuccess:(TextMessage textMessage) { - print("Message sent successfully: ${textMessage.text}, mentioned users: ${textMessage.mentionedUsers}"); - }, - onError:(CometChatException e) { - print("Message sending failed with exception: $e"); - } -); -``` - - - - - - - -You can mention user in text message and media messages captions - - - -## Mentioned Messages - -By default, the SDK will fetch all the messages irrespective of the fact that the logged-in user is mentioned or not in the message. The SDK has other optional filters such as tag info and blocked info. - -| Setting | Description | Default Value | -| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -| `mentionsWithTagInfo(bool value)` | If set to `true`, SDK will fetch a list of messages where users are mentioned & will also fetch the tags of the mentioned users. | false | -| `mentionsWithBlockedInfo(bool value)` | If set to `true`, SDK will fetch a list of messages where users are mentioned & will also fetch their blocked relationship with the logged-in user. | false | - -## Mentions With Tag Info - -To get a list of messages in a conversation where users are mentioned along with the tags of the mentioned users. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messagesRequest = (MessagesRequestBuilder() -..limit=50 -..uid=UID -..mentionsWithTagInfo(true)) -.build(); - -messagesRequest.fetchPrevious(onSuccess:(List messages) { - for (BaseMessage message in messages){ - for (User user in message.mentionedUsers){ - print("mentioned user tags: ${user.tags}"); - } - } - }, - onError: (CometChatException e) { - print("error: $e"); - } -); -``` - - - - -```dart -String GUID = "cometchat-guid-1"; - -MessagesRequest messagesRequest = (MessagesRequestBuilder() -..limit=50 -..guid=GUID -..mentionsWithTagInfo(true)) -.build(); - -messagesRequest.fetchPrevious(onSuccess:(List messages) { - for (BaseMessage message in messages){ - for (User user in message.mentionedUsers){ - print("mentioned user tags: ${user.tags}"); - } - } - }, - onError: (CometChatException e) { - print("error: $e"); - } -); -``` - - - - - -## Mentions With Blocked Info - -To get a list of messages in a conversation where users are mentioned along with the blocked relationship of the mentioned users with the logged-in user. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messagesRequest = (MessagesRequestBuilder() -..limit=50 -..uid=UID -..mentionsWithBlockedInfo(true)) -.build(); - -messagesRequest.fetchPrevious(onSuccess:(List messages) { - for (BaseMessage message in messages){ - for (User user in message.mentionedUsers){ - print("mentioned user has blocked me? ${user.hasBlockedMe}"); - print("mentioned user is blocked by me? ${user.blockedByMe}"); - } - } - }, - onError: (CometChatException e) { - print("error: $e"); - } -); -``` - - - - -```dart -String GUID = "cometchat-guid-1"; - -MessagesRequest messagesRequest = (MessagesRequestBuilder() -..limit=50 -..guid=GUID -..mentionsWithBlockedInfo(true)) -.build(); - -messagesRequest.fetchPrevious(onSuccess:(List messages) { - for (BaseMessage message in messages){ - for (User user in message.mentionedUsers){ - print("mentioned user has blocked me? ${user.hasBlockedMe}"); - print("mentioned user is blocked by me? ${user.blockedByMe}"); - } - } - }, - onError: (CometChatException e) { - print("error: $e"); - } -); -``` - - - - - -## Get Users Mentioned In a Particular Message - -To retrieve the list of users mentioned in the particular message, you can use the `mentionedUsers` property on any `BaseMessage`. This property will return an array containing User objects of the mentioned users, or an empty array if no users were mentioned in the message. - - - -```dart -message.mentionedUsers -``` - - - - - -## Check if Logged-in user has been mentioned - -To check if the logged-in user has been mentioned in a particular message we can use the `hasMentionedMe` property on any `BaseMessage`. This property will return a boolean value, true if the logged-in user has been mentioned, otherwise `false`. - - - -```dart -message.hasMentionedMe -``` - - - - diff --git a/sdk/flutter/v5/overview.mdx b/sdk/flutter/v5/overview.mdx deleted file mode 100644 index 05e859b8a..000000000 --- a/sdk/flutter/v5/overview.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: "Chat SDK" -sidebarTitle: "Overview" ---- - -The CometChat Chat SDK for Flutter enables real-time messaging, user management, group conversations, and more in your Flutter application. Built as a pure Dart implementation in v5, it removes the dependency on platform channels and works seamlessly across Android, iOS, Web, and desktop. - - -This is a **beta release** of the CometChat Flutter Chat SDK v5. APIs and features may change before the stable release. - - - -**Faster Integration with UI Kits** - -If you're using CometChat UI Kits, messaging features can be quickly integrated with pre-built components: -- Conversation lists, message composers, and thread views -- Typing indicators, read receipts, and reactions -- Group management and user profiles - -👉 [Flutter UI Kit Overview](/ui-kit/flutter/overview) - -Use this Chat SDK directly only if you need custom UI or advanced control. - - - -**Upgrading from v4?** - -If you're migrating an existing app from CometChat SDK v4, check out the [Upgrading from v4](/sdk/flutter/v5/upgrading-from-v4-guide) guide for breaking changes, deprecated methods, and migration instructions. - - -Before integrating the Chat SDK, ensure you have a [CometChat Account](https://app.cometchat.com/signup) with your App ID, Region, and Auth Key. Flutter SDK `>=1.2` is required, with Android API Level 21+ and iOS 11+. Users must exist in CometChat to send or receive messages — see [Authentication](/sdk/flutter/v5/authentication-overview) for details. - -```mermaid -sequenceDiagram - participant App - participant CometChat - - App->>CometChat: init(appId, appSettings) - App->>CometChat: login(uid, authKey) - CometChat-->>App: User - App->>CometChat: sendMessage() / addMessageListener() - CometChat-->>App: Real-time events (messages, typing, presence) - App->>CometChat: logout() -``` - - - - - Send text, media, and custom messages in 1-on-1 or group conversations - - - - Create, join, and manage group conversations with member roles and scopes - - - - Listen for messages, typing indicators, read receipts, and presence changes in real time - - - - Show real-time typing status for users and groups - - - - Track online/offline status of users with configurable subscription modes - - - - Add and manage emoji reactions on messages - - - - Organize conversations with message threads - - - - Track message delivery and read status - - - - Integrate AI-powered agents into your chat experience - - - - Voice and video calling with built-in UI components - - - - -| Component | Description | -|-----------|-------------| -| `CometChat` | Main entry point for initialization, authentication, messaging, and real-time events | -| `AppSettings` | Configuration for SDK initialization (App ID, Region, presence subscription) | -| `User` | Represents a CometChat user with UID, name, avatar, and metadata | -| `Group` | Represents a group conversation with GUID, type, and member management | -| `BaseMessage` | Base class for all message types (text, media, custom, action) | -| `MessageListener` | Event interface for real-time message, typing, and receipt events | -| `ConnectionListener` | Event interface for WebSocket connection status changes | diff --git a/sdk/flutter/v5/reactions.mdx b/sdk/flutter/v5/reactions.mdx deleted file mode 100644 index 86824cf6c..000000000 --- a/sdk/flutter/v5/reactions.mdx +++ /dev/null @@ -1,213 +0,0 @@ ---- -title: "Reactions" ---- - - - -Enhance user engagement in your chat application with message reactions. Users can express their emotions using reactions to messages. This feature allows users to add or remove reactions, and to fetch all reactions on a message. You can also listen to reaction events in real-time. Let's see how to work with reactions in CometChat's Flutter SDK. - -## Add a Reaction -Users can add a reaction to a message by calling `addReaction` with the message ID and the reaction emoji. - - - -```dart -int messageId = 1; - -CometChat.addReaction(messageId, "😴", onSuccess: (message) { - debugPrint("Success : ${message.getReactions().last}"); -}, onError: (e) { - debugPrint("Error: ${e.message}"); -}); -``` - - - - - -You can react on Text, Media and Custom messages. - - -## Remove a Reaction -Removing a reaction from a message can be done using the `removeReaction` method. - - - -```dart - -int messageId = 1; - -CometChat.removeReaction(messageId, "😴", onSuccess: (message) { - debugPrint("Success : ${message.getReactions().last}"); -}, onError: (e) { - debugPrint("Error: ${e.message}"); -}); -``` - - - - - -## Fetch Reactions for a Message -To get all reactions for a specific message, first create a `ReactionRequest` using `ReactionRequestBuilder`. You can specify the number of reactions to fetch with `limit` with max limit 100. For this, you will require the ID of the message. This ID needs to be passed to the `messageId` property of the builder class. The `reaction` property will allow you to fetch details for specific reaction or emoji. - - -| Setting | Description | -|-----------------------------------|------------------------------------------------------------------------------------------------------------------| -| `setMessageId(int value)` | Specifies the unique identifier of the message for which you want to fetch reactions. This parameter is mandatory as it tells the SDK which message's reactions are being requested. | -| `setReaction(String value)` | Filters the reactions fetched by the specified reaction type (e.g., "😊", "😂", "👍"). When set, this method will cause the ReactionRequest to only retrieve details of the provided reaction for the given message. | - - -## Fetch Next -The fetchNext() method fetches the next set of reactions for the message. - - - - - -```dart -int messageId = 1; - -ReactionRequest reactionRequest = (ReactionRequestBuilder()..limit = 30..messageId = messageId).build(); - -reactionRequest.fetchNext( - onSuccess: (messageReactions) { - for (MessageReaction messageReaction in messageReactions) { - debugPrint("Success: ${messageReaction.getReactions()}"); - } - }, - onError: (e) { - debugPrint("Error: ${e.message}"); - }, -); -``` - - - - -## Fetch Previous -The `fetchPrevious()` method fetches the previous set of reactions for the message. - - - -```dart -int messageId = 1; - -ReactionRequest reactionRequest = (ReactionRequestBuilder()..limit = 30..messageId = messageId).build(); - -reactionRequest.fetchPrevious( - onSuccess: (messageReactions) { - for (MessageReaction messageReaction in messageReactions) { - debugPrint("Success: ${messageReaction.getReactions()}"); - } - }, - onError: (e) { - debugPrint("Error: ${e.message}"); - }, -); -``` - - - - -## Real-time Reaction Events -Keep the chat interactive with real-time updates for reactions. Register a listener for these events and make your UI responsive. - - - - -```dart -class MyClass with MessageListener { - - String listenerID = "UNIQUE_LISTENER_ID"; - - MyClass() { - CometChat.addMessageListener(listenerID, this); - } - - @override - void onMessageReactionAdded(ReactionEvent reactionEvent) { - debugPrint("Reaction added ${reactionEvent.reaction}"); - } - - @override - void onMessageReactionRemoved(ReactionEvent reactionEvent) { - debugPrint("Reaction removed ${reactionEvent.reaction}"); - } -} -``` - - - - - - -## Removing a Reaction Listener -To stop listening for reaction events, remove the listener as follows: - - - -```dart -String listenerID = "UNIQUE_LISTENER_ID"; - -CometChat.removeMessageReactionListener(listenerID); -``` - - - - -## Get Reactions List -To retrieve the list of reactions reacted on particular message, you can use the `reactions` property on `TextMessage`, `MediaMessage` and `CustomMessage`. This property will return a List of `ReactionCount` containing the reactions, or an empty list if no one reacted on the message. - - - -```dart -message.reactions -``` - - - - - -## Check if Logged-in User has Reacted on Message -To check if the logged-in user has reacted on a particular message or not, You can use the `reactedByMe` property on any `ReactionCount` object. This method will return a boolean value, `true` if the logged-in user has reacted on that message, otherwise `false`. - - - -```dart -for (ReactionCount reactionCount in message.reactions) { - debugPrint("isReactedByMe ${reactionCount.reactedByMe}"); //Return true is logged-in user reacted on that message, otherwise false -} -``` - - - - -## Updated Message With Reaction Info -When a user adds or removes a reaction, you will receive a real-time event. Once you receive the real time event you would want to update the message with the latest reaction information. To do so you can use the `updateMessageWithReactionInfo()` method. - -The `updateMessageWithReactionInfo()` method provides a seamless way to update the reactions on a message instance (`BaseMessage`) in real-time. This method ensures that when a reaction is added or removed from a message, the BaseMessage object's `getReactions()` property reflects this change immediately. - -When you receive a real-time reaction event (MessageReaction), call the `updateMessageWithReactionInfo()` method, passing the BaseMessage instance (message), event data (MessageReaction) and reaction event action type (`ReactionAction.REACTION_ADDED` or `ReactionAction.REACTION_REMOVED`) that corresponds to the message being reacted to. - - - -```dart -// The message to which the reaction is related -BaseMessage message = ...; - -// The reaction event data received in real-time -MessageReaction messageReaction = ...; - -// The recieved reaction event real-time action type. Can be CometChatConstants.REACTION_ADDED or CometChatConstants.REACTION_REMOVED -var action = CometChatConstants.REACTION_ADDED; - -BaseMessage modifiedBaseMessage = await CometChatHelper.updateMessageWithReactionInfo( - baseMessage, - messageReaction, - action -); -``` - - - diff --git a/sdk/flutter/v5/real-time-listeners.mdx b/sdk/flutter/v5/real-time-listeners.mdx deleted file mode 100644 index e1685bb8e..000000000 --- a/sdk/flutter/v5/real-time-listeners.mdx +++ /dev/null @@ -1,249 +0,0 @@ ---- -title: "All Real Time Listeners" ---- - - - -CometChat provides 4 listeners viz. - -1. [UserListener](#user-listener) -2. [GroupListener](#group-listener) -3. [MessageListener](#message-listener) - -## User Listener - -The `UserListener` class provides you with live events related to users. Below are the callback methods provided by the `UserListener` class. - -| Method | Information | -| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `onUserOnline(User user)` | This method is triggered when a user comes online and is available to chat. The details of the user can be obtained from the user object received as the method parameter. | -| `onUserOffline(User user)` | This method is triggered when a user goes offline. The details of the user can be obtained from the User object received as the parameter. | - -To add the `UserListener`, you need to use the `addUserListener()` method provided by the `CometChat` class. - - - -```dart -class Class_Name with UserListener { - //CometChat.addUserListener("user_Listener_id", this); - @override - void onUserOnline(User user) { - - } - - @override - void onUserOffline(User user) { - - } - -} -``` - - - - - -where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. - -Once the activity/fragment where the `UserListener` is declared is not in use, you need to remove the listener using the `removeUserListener()` method which takes the id of the listener to be removed as the parameter. We suggest you call this method in the `onPause()` method of the activity/fragment. - - - -```dart -CometChat.removeUserListener(UNIQUE_LISTENER_ID) -``` - - - - - -## Group Listener - -The `GroupListener` class provides you with all the real-time events related to groups. Below are the callback methods provided by the `GroupListener` class. - -| Method | Information | -| ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| `onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup)` | This method is triggered when a user joins any group. All the members present in the group will receive this event. | -| `onGroupMemberLeft(Action action, User leftUser, Group leftGroup)` | This method is triggered when a user who was a member of any group leaves the group. All the members of the group receive this event. | -| `onGroupMemberKicked(Action action, User kickedUser, User kickedBy, Group kickedFrom)` | This method is triggered when a user is kicked from a group. All the members including the user receive this event | -| `onGroupMemberBanned(Action action, User bannedUser, User bannedBy, Group bannedFrom)` | This method is triggered when a user is banned from a group. All the members including the user receive this event | -| `onGroupMemberUnbanned(Action action, User unbannedUser, User unbannedBy, Group unbannedFrom)` | This method is triggered when a user is banned from a group. All the members of the group receive this event. | -| `onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group)` | This method is triggered when the scope of any Group Member has been changed. All the members that are a part of that group receive this event | -| `onMemberAddedToGroup(Action action, User addedBy, User userAdded, Group addedTo)` | This method is triggered when a user is added to any group. All the members including the user himself receive this event. | - -To add the `GroupListener`, you need to use the `addGroupListener()` method provided by the `CometChat` class. - - - -```dart -class Class_Name with GroupListener { - //CometChat.addGroupListener("UNIQUE_LISTENER_ID", this); - - @override - void onGroupMemberJoined(Action action, User joinedUser, Group joinedGroup) { - - } - - @override - void onGroupMemberLeft(Action action, User leftUser, Group leftGroup) { - - } - - @override - void onGroupMemberKicked(Action action, User kickedUser, User kickedBy, Group kickedFrom) { - - } - - @override - void onGroupMemberBanned(Action action, User bannedUser, User bannedBy, Group bannedFrom) { - - } - - @override - void onGroupMemberUnbanned(Action action, User unbannedUser, User unbannedBy, Group unbannedFrom) { - - } - - @override - void onGroupMemberScopeChanged(Action action, User updatedBy, User updatedUser, String scopeChangedTo, String scopeChangedFrom, Group group) { - - } - - @override - void onMemberAddedToGroup(Action action, User addedby, User userAdded, Group addedTo) { - - } - -} -``` - - - - - -where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. - -Once the activity/fragment where the `GroupListener` is declared is not in use, you need to remove the listener using the `removeGroupListener()` method which takes the id of the listener to be removed as the parameter. We suggest you call this method in the `onPause()` method of the activity/fragment. - - - -```dart -CometChat.removeGroupListener(UNIQUE_LISTENER_ID) -``` - - - - - -## Message Listener - -The `MessageListener` class provides you with live events related to messages. Below are the callback methods provided by the `MessageListener` class. - -| Method | Information | -| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -| `onTextMessageReceived(TextMessage message)` | This event is triggered when a Text Message is received. | -| `onMediaMessageReceived(MediaMessage message)` | This event is triggered when a Media Message is received. | -| `onCustomMessageReceived(CustomMessage message)` | This event is triggered when a Custom Message is received. | -| `onTypingStarted(TypingIndicator typingIndicator)` | This event is triggered when a user starts typing in a user/group conversation | -| `onTypingEnded(TypingIndicator typingIndicator)` | This event is triggered when a user stops typing in a user/group conversation. | -| `onMessagesDelivered(MessageReceipt messageReceipt)` | This event is triggered when a set of messages are marked as delivered for any particular conversation. | -| `onMessagesRead(MessageReceipt messageReceipt)` | This event is triggered when a set of messages are marked as read for any particular conversation. | -| `onMessageEdited(BaseMessage message)` | This method is triggered when a particular message has been edited in a user/group conversation. | -| `onMessageDeleted(BaseMessage message)` | This event is triggered when a particular message is deleted in a user/group conversation. | -| `onInteractiveMessageReceived(InteractiveMessage message)` | This event is triggered when an Interactive Message is received. | -| `onInteractionGoalCompleted(InteractionReceipt receipt)` | This event is triggered when an interaction Goal is achieved. | -| `onTransientMessageReceived(TransientMessage transientMessage)` | This event is triggered when a Transient Message is received. | -| `onMessageReactionAdded(ReactionEvent reactionEvent)` | This event is triggered when a reaction is added to a message in a user/group conversation. | -| `onMessageReactionRemoved(ReactionEvent reactionEvent)` | This event is triggered when a reaction is remove from a message in a user/group conversation. | - -To add the `MessageListener`, you need to use the `addMessageListener()` method provided by the `CometChat` class. - - - -```dart -class Class_Name with MessageListener { - - //CometChat.addMessageListener("listenerId", this); - @override - void onTextMessageReceived(TextMessage textMessage) { - - } - - @override - void onMediaMessageReceived(MediaMessage mediaMessage) { - - } - - @override - void onCustomMessageReceived(CustomMessage customMessage) { - - } - - @override - void onTypingStarted(TypingIndicator typingIndicator) { - - } - - @override - void onTypingEnded(TypingIndicator typingIndicator) { - - } - - - @override - void onMessagesDelivered(MessageReceipt messageReceipt) { - - } - - @override - void onMessagesRead(MessageReceipt messageReceipt) { - - } - - @override - void onMessageEdited(BaseMessage message) { - - } - - @override - void onMessageDeleted(BaseMessage message) { - - } - - @override - void onInteractionGoalCompleted(InteractionReceipt receipt) { - - - } - - @override - void onInteractiveMessageReceived(InteractiveMessage message) { - - } - - - @Override - public void onTransientMessageReceived(TransientMessage transientMessage) { - - } - - @Override - public void onMessageReactionAdded(ReactionEvent reactionEvent) { - - } - - @Override - public void onMessageReactionRemoved(ReactionEvent reactionEvent) { - - } - -} -``` - - - - - -where `UNIQUE_LISTENER_ID` is the unique identifier for the listener. Please make sure that no two listeners are added with the same listener id as this could lead to unexpected behavior resulting in loss of events. - -Once the activity/fragment where the `MessageListener` is declared is not in use, you need to remove the listener using the `removeMessageListener()` method which takes the id of the listener to be removed as the parameter. We suggest you call this method in the `onPause()` method of the activity/fragment. diff --git a/sdk/flutter/v5/receive-messages.mdx b/sdk/flutter/v5/receive-messages.mdx deleted file mode 100644 index 92246df7f..000000000 --- a/sdk/flutter/v5/receive-messages.mdx +++ /dev/null @@ -1,641 +0,0 @@ ---- -title: "Receive A Message" ---- - - - -Receiving messages with CometChat has two parts: - -1. Adding a listener to receive [Real-time Messages](/sdk/flutter/receive-messages#real-time-messages) when your app is running -2. Calling a method to retrieve [Missed Messages](/sdk/flutter/receive-messages#missed-messages) when your app was not running - -## Real-time Messages - -*In other words, as a recipient, how do I receive messages when my app is running?* - -For every activity or fragment you wish to receive messages in, you need to register the `MessageListener` using the `addMessageListener()` method. - -We suggest adding the listener in the `init` method of the Stateful class or at the initialization of class where you wish to receive these events in. - - - -```dart -class Class_Name with MessageListener { - -//CometChat.addMessageListener("listenerId", this); - @override -void onTextMessageReceived(TextMessage textMessage) { - debugPrint("Text message received successfully: $textMessage"); -} - -@override -void onMediaMessageReceived(MediaMessage mediaMessage) { - debugPrint("Media message received successfully: $mediaMessage"); -} - -@override -void onCustomMessageReceived(CustomMessage customMessage) { - debugPrint("Custom message received successfully: $customMessage"); -} - -@override -onInteractiveMessageReceived(InteractiveMessage message) { - -} - - -} -``` - - - - - -| Parameter | Description | -| ---------- | ---------------------------------------------------------------------------------------------- | -| listenerID | An ID that uniquely identifies that listener. We recommend using the activity or fragment name | - -We recommend you remove the listener once the activity or fragment is not in use. Typically, this can be added in the `dispose()` method. - - - -```dart -private String listenerID = "UNIQUE_LISTENER_ID"; - -CometChat.removeMessageListener(listenerID); -``` - - - - - - - -As a sender, you will not receive your own message in a real-time message event. However, if a user is logged-in using multiple devices, they will receive an event for their own message in other devices. - - - -## Missed Messages - -*In other words, as a recipient, how do I receive messages that I missed when my app was not running?* - -For most use cases, you will need to add functionality to load missed messages. If you're building an on-demand or live streaming app, you may choose to skip this and fetch the message history (say, last 100 messages) instead. - -Using the same `MessagesRequest` class and the filters provided by the `MessagesRequestBuilder` class, you can fetch the message that we sent to the logged-in user but were not delivered to them as they were offline. For this, you will require the ID of the last message received. You can either maintain it at your end or use the `getLastDeliveredMessageId()` method provided by the CometChat class. This ID needs to be passed to the `setMessageId()` method of the builder class. - -Now using the `fetchNext()` method, you can fetch all the messages that were sent to the user when they were offline. - -Calling the `fetchNext()` method on the same object repeatedly allows you to fetch all the offline messages for the logged in user in a paginated manner - -### For a Particular One-on-one Conversation - - - -```dart -int limit = 30; -int lastMessageId = -1; -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..limit = limit - ..messageId = lastMessageId - ).build(); - -messageRequest.fetchNext(onSuccess: (List list) { - for (BaseMessage message in list) { - if(message is TextMessage){ - debugPrint("Text message received successfully: "+(message as TextMessage).toString()); - }else if(message is MediaMessage){ - debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); - } - } - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - - -### For a Particular Group Conversation - - - -```dart -int limit = 30; -int lastMessageId = -1; -String GUID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..guid = GUID - ..limit = limit - ..messageId = lastMessageId - ).build(); - -messageRequest.fetchNext(onSuccess: (List list) { - for (BaseMessage message in list) { - if(message is TextMessage){ - debugPrint("Text message received successfully: "+(message as TextMessage).toString()); - }else if(message is MediaMessage){ - debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); - } - } - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - - -## Unread Messages - -*In other words, as a logged-in user, how do I fetch the messages I've not read?* - -Using the `MessagesRequest` class described above, you can fetch the unread messages for the logged-in user. In order to achieve this, you need to set the `unread` variable in the builder to `true` so that only the unread messages are fetched. - -### For a Particular One-on-one Conversation - - - -```dart -int limit = 30; -int lastMessageId = -1; -String GUID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..guid = GUID - ..limit = limit - ..messageId = lastMessageId - ..unread = true - ).build(); - -messageRequest.fetchPrevious(onSuccess: (List list) { - for (BaseMessage message in list) { - if(message is TextMessage){ - debugPrint("Text message received successfully: "+(message as TextMessage).toString()); - }else if(message is MediaMessage){ - debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); - } - } - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - - -### For a Particular Group Conversation - - - -```dart -int limit = 30; -int lastMessageId = -1; -String GUID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..guid = GUID - ..limit = limit - ..messageId = lastMessageId - ..unread = true - ).build(); - -messageRequest.fetchPrevious(onSuccess: (List list) { - for (BaseMessage message in list) { - if(message is TextMessage){ - debugPrint("Text message received successfully: "+(message as TextMessage).toString()); - }else if(message is MediaMessage){ - debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); - } - } - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - - - -Base Message - -The list of messages received is in the form of objects of `BaseMessage` class. A `BaseMessage` can either be an object of the `TextMessage`, `MediaMessage`, `CustomMessage`, `Action` or `Call` class. You can use the `is` operator to check the type of object. - - - -## Message History - -*In other words, as a logged-in user, how do I fetch the message history for a user or a group conversation?* - -### For a Particular One-on-one Conversation - -Using the `MessagesRequest` class and the filters for the `MessagesRequestBuilder` class as shown in the below code snippet, you can fetch the entire conversation between the logged in user and any particular user. For this use case, it is mandatory to set the UID parameter of the builder. This UID is the unique id of the user for which the conversation needs to be fetched. - - - -```dart -int limit = 30; -int lastMessageId = -1; -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..limit = limit - ).build(); - -messageRequest.fetchPrevious(onSuccess: (List list) { - for (BaseMessage message in list) { - if(message is TextMessage){ - debugPrint("Text message received successfully: "+(message as TextMessage).toString()); - }else if(message is MediaMessage){ - debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); - } - } - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - - -Calling the `fetchPrevious()` method on the same object repeatedly allows you to fetch all the previous messages in a paginated way. - -### For a Particular Group Conversation - -Using the `MessagesRequest` class and the filters for the `MessagesRequestBuilder` class as shown in the below code snippet, you can fetch the entire conversation for any group provided you have joined the group. For this use case, it is mandatory to set the GUID parameter of the builder. This GUID is the unique identifier of the Group for which the messages are to be fetched. - - - -```dart -int limit = 30; -String GUID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..guid = GUID - ..limit = limit - ).build(); - -messageRequest.fetchPrevious(onSuccess: (List list) { - for (BaseMessage message in list) { - if(message is TextMessage){ - debugPrint("Text message received successfully: "+(message as TextMessage).toString()); - }else if(message is MediaMessage){ - debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); - } - } - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - - -Calling the `fetchPrevious()` method on the same object repeatedly allows you to fetch the entire conversation between the logged in user and the specified user. This can be implemented with upward scrolling to display the entire conversation. - -## Search Messages - -In other words, as a logged-in user, how do I search a message? - -In order to do this, you can use the `searchKeyword` method. This method accepts string as input. When set, the SDK will fetch messages which match the `searchKeyword`. - - - -By default, the searchKey is searched only in message text. However, if you enable `Conversation & Advanced Search`, the searchKey will be searched in message text, file name, mentions & mime type of the file. - -The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) - - - -| Feature | Basic Search | Advance Search | -| ---------------- | ------------ | -------------- | -| Text search | ✅ | ✅ | -| File name search | ❌ | ✅ | -| Mentions search | ❌ | ✅ | -| Mime type search | ❌ | ✅ | - -### For a Particular One-on-one Conversation - - - -```dart -int limit = 30; -int searchKeyword = "search keyboard"; -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..limit = limit - .searchKeyword=searchKeyword - ).build(); - -messageRequest.fetchPrevious(onSuccess: (List list) { - for (BaseMessage message in list) { - if(message is TextMessage){ - debugPrint("Text message received successfully: "+(message as TextMessage).toString()); - }else if(message is MediaMessage){ - debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); - } - } - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - - -### For a Particular Group Conversation - - - -```dart -int limit = 30; -int searchKeyword = "search keyboard"; -String GUID = "cometchat-guid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..guid = GUID - ..limit = limit - .searchKeyword=searchKeyword - ).build(); - -messageRequest.fetchPrevious(onSuccess: (List list) { - for (BaseMessage message in list) { - if(message is TextMessage){ - debugPrint("Text message received successfully: "+(message as TextMessage).toString()); - }else if(message is MediaMessage){ - debugPrint("Media message received successfully: "+(message as MediaMessage).toString()); - } - } - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - - -## Unread Messages Count - -*In other words, as a logged-in user, how do I find out the number of unread messages that I have?* - -### For a Particular One-on-one Conversation - -*In other words, how do I find out the number of unread messages I have from a particular user?* - -In order to get the unread message count for a particular user (with respect to the logged-in user), you can use the `getUnreadMessageCountForUser()`. - -This method has the two variants: - - - -```dart -CometChat.getUnreadMessageCountForUser(String UID, Callbacks); -``` - - - - - -If you wish to ignore the messages from blocked users you can use the below syntax setting the boolean parameter to `true`: - - - -```dart -CometChat.getUnreadMessageCountForUser(String UID, boolean hideMessagesFromBlockedUsers, Callbacks); -``` - - - - - - - -```dart -private String UID = "cometchat-uid-1" - -CometChat.getUnreadMessageCountForUser(UID, new CometChat.CallbackListener>() { -@Override - public void onSuccess(HashMap stringIntegerHashMap) { - // handle success -} - -@Override - public void onError(CometChatException e) { - // handle error -} -}); -``` - - - - - -In the `onSuccess()` callback, you will receive a `Map` which will contain the `UID` of the user as the key and the unread message count as the value. - -### For a Particular Group Conversation - -*In other words, how do I find out the number of unread messages I have in a single group?* - -In order to get the unread message count for a particular group, you can use the `getUnreadMessageCountForGroup()`. - -This method has two variants: - - - -```dart -Map? ab = await CometChat.getUnreadMessageCountForGroup(guid: guid); -``` - - - - - -If you wish to ignore the messages from blocked users you can use the following syntax setting the boolean parameter to `true`: - - - -```dart -Map? ab = await CometChat.getUnreadMessageCountForGroup(guid: guid,hideMessagesFromBlockedUsers: hideBlockedUser); -``` - - - - - - - -```dart -private String GUID = "cometchat-guid-1" - -CometChat.getUnreadMessageCountForGroup(guid: GUID,hideMessagesFromBlockedUsers: hideBlockedUser, - onSuccess: (Map map) { - - }, - onError: (CometChatException e) { - - }); -``` - - - - - -In the `onSuccess()` callback, you will receive a `Map` which will contain the `GUID` of the group as the key and the unread message count as the value. - -### For All One-on-one & Group Conversations - -*In other words, how do I find out the number of unread messages for every one-on-one and group conversation?* - -In order to get all the unread message counts, you can use the `getUnreadMessageCount()` method. This method has two variants: - - - -```dart -await CometChat.getUnreadMessageCount(); -``` - - - - - -If you wish to ignore the messages from blocked users you can use the following syntax setting the boolean parameter to `true`: - - - -```dart -await CometChat.getUnreadMessageCount(hideMessagesFromBlockedUsers: true); -``` - - - - - - - -```dart -CometChat.getUnreadMessageCount(hideMessagesFromBlockedUsers: hideBlockedUser, - onSuccess: (Map> map) { - - },onError: (CometChatException e) { - - }); -``` - - - - - -In the `onSuccess()` callback, you will receive a `Map` having two keys: - -1. `user` - The value for this key holds another `Map` that holds the `UIDs` of users and their corresponding unread message counts. -2. `group` - The value for this key holds another `Map` that holds the `GUIDs` of groups and their corresponding unread message counts. - -### For Unread Count for all One-on-one Conversations - -*In other words, how do I find out the number of unread messages I have for every user?* - -In order to fetch the unread message counts for just the users, you can use the `getUnreadMessageCountForAllUsers()` method. - -This method, just like others, has two variants: - - - -```dart -await CometChat.getUnreadMessageCountForAllUsers(); -``` - - - - - -If you wish to ignore the messages from blocked users you can use the following syntax setting the boolean parameter to `true`: - - - -```dart -await CometChat.getUnreadMessageCountForAllUsers(hideMessagesFromBlockedUsers: true); -``` - - - - - - - -```dart -CometChat.getUnreadMessageCountForAllUsers(hideMessagesFromBlockedUsers: hideMessagesFromBlockedUsers, - onSuccess: (Map map) { - - }, - onError: (CometChatException e) { - - }); -``` - - - - - -In the `onSuccess()` callback, you will receive a `Map` that will contain the `UIDs` of users as the key and the unread message counts as the values. - -### Fetch Unread Count for all Group Conversations - -*In other words, how do I find out the number of unread messages for every group?* - -In order to fetch the unread message counts for all groups, you can use the `getUnreadMessageCountForAllGroups()` method. - -This method has two variants: - - - -```dart -await CometChat.getUnreadMessageCountForAllGroups(); -``` - - - - - -If you wish to ignore the messages from blocked users you can use the below syntax setting the boolean parameter to `true`: - - - -```dart -await CometChat.getUnreadMessageCountForAllGroups(hideMessagesFromBlockedUsers: true); -``` - - - - - - - -```dart -CometChat.getUnreadMessageCountForAllGroups(hideMessagesFromBlockedUsers: hideMessagesFromBlockedUsers, - onSuccess: (Map map) { - - }, - onError: (CometChatException e) { - - }); -``` - - - - - -In the `onSuccess()` callback, you will receive a `Map` which will contain the `GUIDs` of the groups as the key and the unread message counts as the values. diff --git a/sdk/flutter/v5/retrieve-conversations.mdx b/sdk/flutter/v5/retrieve-conversations.mdx deleted file mode 100644 index 690d1d42b..000000000 --- a/sdk/flutter/v5/retrieve-conversations.mdx +++ /dev/null @@ -1,384 +0,0 @@ ---- -title: "Retrieve Conversations" ---- - - - -## Retrieve List of Conversations - -*In other words, as a logged-in user, how do I retrieve the latest conversations that I've been a part of?* - -To fetch the list of conversations, you can use the `ConversationsRequest` class. To use this class i.e. to create an object of the `ConversationsRequest` class, you need to use the `ConversationsRequestBuilder` class. The `ConversationsRequestBuilder` class allows you to set the parameters based on which the conversations are to be fetched. - -The `ConversationsRequestBuilder` class allows you to set the below parameters: - -### Set Limit - -This method sets the limit i.e. the number of conversations that should be fetched in a single iteration. - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 -).build(); -``` - - - - - -### Set Conversation Type - -This method can be used to fetch user or group conversations specifically. The `conversationType` variable can hold one of the below two values: - -* user - Only fetches user conversation. -* group - Only fetches group conversations. - -If none is set, the list of conversations will include both user and group conversations. - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..conversationType = CometChatConversationType.user -).build(); -``` - - - - - -### With User and Group Tags - -This method can be used to fetch the user/group tags in the `Conversation` Object. By default the value is false. - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..withUserAndGroupTags = true -).build(); -``` - - - - - -### With Tags - -This method makes sure that the tags associated with the conversations are returned along with the other details of the conversations. The default value for this parameter is `false` - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..withTags = true -).build(); -``` - - - - - -### Set Tags - -This method helps you fetch the conversations based on the specified tags. - - - -```dart -List tags = []; -tags.add("archived"); -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..tags = tags -).build(); -``` - - - - - -### Include Blocked Users - -This method helps you fetch the conversations of users whom the logged-in user has blocked. - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..includeBlockedUsers = true -).build(); -``` - - - - - -### With Blocked Info - -This method can be used to fetch the blocked information of the blocked user in the `ConversationWith` object. - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..withBlockedInfo = true -).build(); -``` - - - - - -### Search Conversations - -This method helps you search a conversation based on User or Group name. - - - -This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) - - - - - -```dart - ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..searchKeyword = "Hiking" - ).build(); -``` - - - - - -### Unread Conversations - -This method helps you fetch unread conversations. - - - -This feature is only available with `Conversation & Advanced Search`. The `Conversation & Advanced Search` is only available in `Advanced` & `Custom` [plans](https://www.cometchat.com/pricing). If you're already on one of these plans, please enable the `Conversation & Advanced Search` from [CometChat Dashboard](https://app.cometchat.com) (Open your app, navigate to Chats -> Settings -> General Configuration) - - - - - -```dart - ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..unread = true - ).build(); -``` - - - - - -### Filter by User Tags - -This method helps you filter conversations based on specific user tags. - - - -```dart -List userTags = ["premium", "verified"]; -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..userTags = userTags -).build(); -``` - - - - - -### Filter by Group Tags - -This method helps you filter conversations based on specific group tags. - - - -```dart -List groupTags = ["support", "sales"]; -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..groupTags = groupTags -).build(); -``` - - - - - -### Hide Agentic Conversations - -This method helps you hide AI-driven (agentic) conversations from the list. Agentic conversations are those involving AI agents. - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..hideAgentic = true -).build(); -``` - - - - - -### Only Agentic Conversations - -This method helps you fetch only AI-driven (agentic) conversations. This is useful when you want to display a separate list of AI agent conversations. - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ..onlyAgentic = true -).build(); -``` - - - - - -Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `ConversationsRequest` class. - -Once you have the object of the `ConversationsRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `Conversation` objects containing X number of conversations depending on the limit set. - -A Maximum of only 50 Conversations can be fetched at once. - - - -```dart -ConversationsRequest conversationRequest = (ConversationsRequestBuilder() - ..limit = 50 - ).build(); - -conversationRequest.fetchNext( - onSuccess: (List conversations){ - - - }, onError: (CometChatException e){ - - } -``` - - - - - -The `Conversation`object consists of the below fields: - -| Field | Information | -| --------------------- | -------------------------------------------------------------------- | -| `conversationId` | ID of the conversation | -| `conversationType` | Type of conversation (user/group) | -| `lastMessage` | Last message the conversation | -| `conversationWith` | `User` or `Group` object containing the details of the user or group | -| `unreadMessageCount` | Unread message count for the conversation | -| `unreadMentionsCount` | the count of unread mentions in the conversation. | -| `lastReadMessageId` | the ID of the last read message in the conversation (int type). | - -## Tag Conversation - -*In other words, as a logged-in user, how do I tag a conversation?* - -In order to tag a specific conversation, you can use the `tagConversation()` method. The `tagConversation()` method accepts three parameters. - -1. `conversationWith`: UID/GUID of the user/group whose conversation you want to fetch. - -2. `conversationType`: The `conversationType` variable can hold one of the below two values: - - 1. user - Only fetches user conversation. - 2. group - Only fetches group conversations. - -3. `tags`: The `tags` variable will be a list of tags you want to add to a conversation. - - - -```dart -String conversationWith = "cometchat-uid-1"; //id of the user/group -String conversationType = "user"; -List tags = []; -tags.add("archived"); - -CometChat.tagConversation(conversationWith, conversationType, tags, - onSuccess: (Conversation conversation) { - debugPrint("Conversation tagged Successfully : $conversation"); - }, onError: (CometChatException e) { - debugPrint("Conversation tagging failed : ${e.message}"); - } -); -``` - - - - - - - -The tags for conversations are one-way. This means that if user A tags a conversation with user B, that tag will be applied to that conversation only for user A. - - - -## Retrieve Single Conversation - -*In other words, as a logged-in user, how do I retrieve a specific conversation?* - -In order to fetch a specific conversation, you can use the `getConversation` method. The `getConversation` method accepts two parameters. - -1. `conversationWith`: UID/GUID of the user/group whose conversation you want to fetch. -2. `conversationType`: The `conversationType` variable can hold one of the below two values: - -* user - Only fetches user conversation. -* group - Only fetches group conversations. - - - -```dart -String conversationWith = "cometchat-uid-1"; //id of the user/group -String conversationType = "user"; -CometChat.getConversation(conversationWith, conversationType, - onSuccess: (Conversation conversation) { - debugPrint("Fetch Conversation Successfully : $conversation"); - }, onError: (CometChatException e) { - debugPrint("Fetch Conversation failed : ${e.message}"); - } -); -``` - - - - - -## Convert Messages to Conversations - -As per our [Receive Messages](/sdk/flutter/receive-messages) guide, for real-time messages, you will always receive `Message` objects and not `Conversation` objects. Thus, you will need a mechanism to convert the `Message` object to a `Conversation` object. You can use the `getConversationFromMessage` method for this purpose. - - - -```dart -Conversation conversation = CometChat.getConversationFromMessage(message); -``` - - - - - - - -While converting `Message` object to `Conversation` object, the `unreadMessageCount` & `tags` will not be available in the `Conversation` object. The unread message count needs to be managed in your client-side code. - - diff --git a/sdk/flutter/v5/retrieve-group-members.mdx b/sdk/flutter/v5/retrieve-group-members.mdx deleted file mode 100644 index 517799414..000000000 --- a/sdk/flutter/v5/retrieve-group-members.mdx +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: "Retrieve Group Members" ---- - - - -## Retrieve the List of Group Members - -In order to fetch the list of groups members for a group, you can use the `GroupMembersRequest` class. To use this class i.e to create an object of the `GroupMembersRequest` class, you need to use the `GroupMembersRequestBuilder` class. The `GroupMembersRequestBuilder` class allows you to set the parameters based on which the groups are to be fetched. - -The `GroupMembersRequestBuilder` class allows you to set the below parameters: - -The `GUID` of the group for which the members are to be fetched must be specified in the constructor of the `GroupMembersRequestBuilder` class. - -### Set Limit - -This method sets the limit i.e. the number of members that should be fetched in a single iteration. - - - -```dart -String GUID = "GUID"; - GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) - ..limit = 20 - ).build(); -``` - - - - - -### Set Search Keyword - -This method allows you to set the search string based on which the group members are to be fetched. - - - -```dart -String GUID = "GUID"; -GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) - ..limit = 20 - ..searchKeyword = "abc" - ).build(); -``` - - - - - -### Set Scopes - -This method allows you to fetch group members based on the specified scopes. - - - -```dart -List scopes =[]; -scopes.add("admin"); -scopes.add("moderator"); -String GUID = "GUID"; -GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) - ..limit = 20 - ..scopes = scopes - ).build(); -``` - - - - - -### Set Status - -This method allows you to filter group members based on their online/offline status. - - - -```dart -String GUID = "GUID"; -GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) - ..limit = 20 - ..status = CometChatUserStatus.online // or CometChatUserStatus.offline - ).build(); -``` - - - - - -The `status` parameter can contain one of the below two values: -* `CometChatUserStatus.online` - will return only online group members. -* `CometChatUserStatus.offline` - will return only offline group members. - -If this parameter is not set, all group members will be returned regardless of their status. - -Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `GroupMembersRequest` class. - -Once you have the object of the `GroupMembersRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `GroupMember` objects containing N number of members depending on the limit set. - - - -```dart -String GUID = "GUID"; -GroupMembersRequest groupMembersRequest = (GroupMembersRequestBuilder(GUID) - ..limit = 20 - ).build(); - -groupMembersRequest.fetchNext(onSuccess: (List groupMemberList){ - debugPrint("Group Members fetched Successfully : $groupMemberList "); - }, onError: (CometChatException e) { - debugPrint("Delete Group failed with exception: ${e.message}"); - }); -``` - - - - diff --git a/sdk/flutter/v5/retrieve-groups.mdx b/sdk/flutter/v5/retrieve-groups.mdx deleted file mode 100644 index 23417e0b0..000000000 --- a/sdk/flutter/v5/retrieve-groups.mdx +++ /dev/null @@ -1,174 +0,0 @@ ---- -title: "Retrieve Groups" ---- - - - -## Retrieve List of Groups - -*In other words, as a logged-in user, how do I retrieve the list of groups I've joined and groups that are available?* - -In order to fetch the list of groups, you can use the `GroupsRequest` class. To use this class i.e to create an object of the `GroupsRequest` class, you need to use the `GroupsRequestBuilder` class. The `GroupsRequestBuilder` class allows you to set the parameters based on which the groups are to be fetched. - -### Set Limit - -This method sets the limit i.e. the number of groups that should be fetched in a single iteration. - - - -```dart -GroupsRequest groupsRequest = (GroupsRequestBuilder() - ..limit= 20 -).build(); -``` - - - - - -### Set Search Keyword - -This method allows you to set the search string based on which the groups are to be fetched. - - - -```dart -GroupsRequest groupsRequest = (GroupsRequestBuilder() - ..limit= 20 - ..searchKeyword = "abc" -).build(); -``` - - - - - -### Joined Only - -This method when used, will ask CometChat to only return the groups that the user has joined or is a part of. - - - -```dart -GroupsRequest groupsRequest = (GroupsRequestBuilder() - ..limit= 20 - ..joinedOnly = true -).build(); -``` - - - - - -### Set Tags - -This method accepts a list of tags based on which the list of groups is to be fetched. The list fetched will only contain the groups that have been tagged with the specified tags. - - - -```dart -List tags =[]; -tags.add("archived"); -GroupsRequest groupsRequest = (GroupsRequestBuilder() - ..limit= 20 - ..tags = tags -).build(); -``` - - - - - -### With Tags - -This property when set to true will fetch tags data along with the list of groups. - - - -```dart -GroupsRequest groupsRequest = (GroupsRequestBuilder() - ..limit= 20 - ..withTags = true -).build(); -``` - - - - - -Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `GroupsRequest` class. - -Once you have the object of the `GroupsRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `Group` objects containing 'n' number of groups depending on the limit set. - -The list of groups fetched will only have the public and password type groups. The private groups will only be available if the user is a member of that private group. - - - -```dart -GroupsRequest groupsRequest = (GroupsRequestBuilder() - ..limit= 20 -).build(); - -groupsRequest.fetchNext(onSuccess: (List groupList) { - debugPrint("Fetched Group Successfully : $groupList "); - }, onError: (CometChatException e) { - debugPrint("Group Request failed with exception: ${e.message}"); - }); -``` - - - - - -## Retrieve Particular Group Details - -*In other words, as a logged-in user, how do I retrieve information for a specific group?* - -To get the information of a group, you can use the `getGroup()` method. - - - -```dart -String GUID = "GUID"; - -CometChat.getGroup(GUID, onSuccess: (Group group) { - debugPrint("Fetched Group Successfully : $group "); - }, onError: (CometChatException e) { - debugPrint("Group Request failed with exception: ${e.message}"); - }); -``` - - - - - -| Parameter | Description | -| --------- | ------------------------------------------------------------ | -| `GUID` | The GUID of the group for whom the details are to be fetched | - -On success, the `Group` object containing the details of the group is returned. - -## Get online group member count - -To get the total count of online users in particular groups, you can use the `getOnlineGroupMemberCount()` method. - - - -```dart -List guids = []; -guids.add("cometchat-guid-1"); -guids.add("cometchat-guid-11"); - -CometChat.getOnlineGroupMemberCount(guids, - onSuccess: (Map count) { - debugPrint("Fetched Online Group Member Count Successfully : $count "); - }, onError: (CometChatException e) { - debugPrint("Online Group Member failed with exception: ${e.message}"); - }); -``` - - - - - -This method returns a `Map` with the GUID of the group as the key and the online member count for that group as the value. diff --git a/sdk/flutter/v5/retrieve-users.mdx b/sdk/flutter/v5/retrieve-users.mdx deleted file mode 100644 index 9284b158f..000000000 --- a/sdk/flutter/v5/retrieve-users.mdx +++ /dev/null @@ -1,337 +0,0 @@ ---- -title: "Retrieve Users" ---- - - - -## Retrieve Logged In User Details - -You can get the details of the logged-in user using the `getLoggedInUser()` method. This method can also be used to check if the user is logged in or not. If the method returns `null`, it indicates that the user is not logged in and you need to log the user into CometChat. - - - -```dart -User user = await CometChat.getLoggedInUser() -``` - - - - - -This method will return a `User` object containing all the information related to the logged-in user. - -## Retrieve List of Users - -In order to fetch the list of users, you can use the `UsersRequest` class. To use this class i.e to create an object of the UsersRequest class, you need to use the `UsersRequestBuilder` class. The `UsersRequestBuilder` class allows you to set the parameters based on which the users are to be fetched. - -The `UsersRequestBuilder` class allows you to set the below parameters: - -### Set Limit - -This method sets the limit i.e. the number of users that should be fetched in a single iteration. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ).build(); -``` - - - - - -### Set Search Keyword - -This method allows you to set the search string based on which the users are to be fetched. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..searchKeyword = "abc" - ).build(); -``` - - - - - -### Set Status - -The status based on which the users are to be fetched. The status parameter can contain one of the below two values: - -* CometChatUserStatus.online - will return the list of only online users. -* CometChatUserStatus.offline - will return the list of only offline users. - -If this parameter is not set, will return all the available users. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..userStatus = CometChatUserStatus.online - ).build(); -``` - - - - - -If this parameter is not set, will return all users. - -### Hide Blocked Users - -This method is used to determine if the blocked users should be returned as a part of the user list. If set to `true`, the user list will not contain the users blocked by the logged-in user. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..hideBlockedUsers = true - ).build(); -``` - - - - - -### Set Roles - -This method allows you to fetch the users based on multiple roles. - - - -```dart -List roles = []; -roles.add("role1"); -roles.add("role2"); -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..roles = roles - ).build(); -``` - - - - - -### Friends Only - -This property when set to true will return only the friends of the logged-in user. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..friendsOnly = true - ).build(); -``` - - - - - -### Set Tags - -This method accepts a list of tags based on which the list of users is to be fetched. The list fetched will only contain the users that have been tagged with the specified tags. - - - -```dart -List tags = []; -tags.add("tag1"); -tags.add("tag2"); -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..tags = tags - ).build(); -``` - - - - - -### With Tags - -This property when set to true will fetch tags data along with the list of users. - - - -```dart -List tags = []; -tags.add("tag1"); -tags.add("tag2"); -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..withTags = true - ).build(); -``` - - - - - -### Set UIDs - -This method accepts a list of UIDs based on which the list of users is fetched. A maximum of 25 users can be fetched. - - - -```dart -List uids = []; -uids.add("UID1"); -uids.add("UID2"); -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 25 - ..uids = uids - ).build(); -``` - - - - - -### Search In - -This method allows you to specify which user fields to search in when using the search keyword. You can search in `name`, `uid`, or both. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..searchKeyword = "john" - ..searchIn = ["name", "uid"] - ).build(); -``` - - - - - -### Sort By - -This method allows you to specify the field by which the users should be sorted. You can sort by `name`, `status`, or `createdAt`. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..sortBy = "name" - ).build(); -``` - - - - - -### Sort By Order - -This method allows you to specify the order in which the users should be sorted. You can use `asc` for ascending or `desc` for descending order. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..sortBy = "name" - ..sortByOrder = "asc" - ).build(); -``` - - - - - -You can combine these parameters for more precise user filtering and ordering: - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 50 - ..searchKeyword = "john" - ..searchIn = ["name"] - ..sortBy = "name" - ..sortByOrder = "asc" - ).build(); -``` - - - - - -Finally, once all the parameters are set to the builder class, you need to call the `build()` method to get the object of the `UsersRequest` class. - -Once you have the object of the `UsersRequest` class, you need to call the `fetchNext()` method. Calling this method will return a list of `User` objects containing 'n' number of users depending on the limit set. - - - -```dart -UsersRequest usersRequest = (UsersRequestBuilder() - ..limit = 25 - ).build(); - -usersRequest.fetchNext(onSuccess: (List userList){ - debugPrint("User List Fetched Successfully : $userList"); - },onError: (CometChatException e){ - debugPrint("User List Fetch Failed: ${e.message}"); - }); -``` - - - - - -## Retrieve Particular User Details - -To get the information of a user, you can use the `getUser()` method. - - - -```dart -String UID = "UID"; - -CometChat.getUser(UID, onSuccess: (User user){ - debugPrint("User Fetched Successfully : $user"); - }, onError: (CometChatException e){ - debugPrint("User Fetch Failed: ${e.message}"); - }); -``` - - - - - -The `getUser()` method takes the following parameters: - -| Parameter | Description | -| --------- | ---------------------------------------------------------- | -| `UID` | The UID of the user for whom the details are to be fetched | - -On success, the `User` object containing the details of the user is returned. - -## Get online user count - -To get the total count of online users for your app, you can use the `getOnlineUserCount()` method. - - - -```dart -CometChat.getOnlineUserCount(onSuccess: (int count){ - debugPrint("Online User Count: $count"); - }, onError: (CometChatException e){ - debugPrint("User Count Fetch Failed: ${e.message}"); - }); -``` - - - - diff --git a/sdk/flutter/v5/send-message.mdx b/sdk/flutter/v5/send-message.mdx deleted file mode 100644 index fd2c833d9..000000000 --- a/sdk/flutter/v5/send-message.mdx +++ /dev/null @@ -1,646 +0,0 @@ ---- -title: "Send A Message" ---- - - - -Using CometChat, you can send three types of messages: - -1. A [Text Message](/sdk/flutter/send-message#text-message), the most common and standard message type. -2. A [Media Message](/sdk/flutter/send-message#media-message), for sending photos, videos and files. -3. A [Custom Message](/sdk/flutter/send-message#custom-message), for sending completely custom data using Map structures. -4. A [Interactive Messages](/sdk/flutter/interactive-messages) , for sending end-user interactive messages of type form, card and custom Interactive - -You can also send metadata along with a text or media message. Think, for example, if you'd want to share the user's location with every message, you can use the metadata field. - -## Text Message - -*In other words, as a sender, how do I send a text message?* - -To send a text message to a single user or group, you need to use the `sendMessage()` method and pass a `TextMessage` object to it. - -### Add Metadata - -To send custom data along with a text message, you can use the `setMetadata` method and pass a `Map` to it. - - - -```dart -Map metadata = {}; -metadata["lattitude"] = "50.6192171633316" ; -metadata["longitude"] = "-72.68182268750002" ; -textMessage.metadata = metadata; -``` - - - - - -### Add Tags - -To add a tag to a message you can assign value in `.tags` variable of the TextMessage Class. `tags` accepts a list of tags. - - - -```dart -List tags = []; -tags.add("pinned"); -textMessage.tags = tags; -``` - - - - - -Once the text message object is ready, you need to use the `sendMessage()` method to send the text message to the recipient. - - - -```dart -String receiverID = "cometchat-uid-1"; -String messageText = "messageText"; -String receiverType = CometChatConversationType.user; -String type = CometChatMessageType.text; - -TextMessage textMessage = TextMessage(text: messageText, - receiverUid: receiverID, - receiverType: receiverType, - type: type); -CometChat.sendMessage(textMessage, - onSuccess: (TextMessage message) { - debugPrint("Message sent successfully: $message"); - }, onError: (CometChatException e) { - debugPrint("Message sending failed with exception: ${e.message}"); - } -); -``` - - - - -```dart -String receiverID = "cometchat-guid-1"; -String messageText = "messageText"; -String receiverType = CometChatConversationType.group; -String type = CometChatMessageType.text; - -TextMessage textMessage = TextMessage(text: messageText, - receiverUid: receiverID, - receiverType: receiverType, - type: type); -CometChat.sendMessage(textMessage, onSuccess: (TextMessage message) { -debugPrint("Message sent successfully: $message"); -}, onError: (CometChatException e) { -debugPrint("Message sending failed with exception: ${e.message}"); -}); -``` - - - - - -The `TextMessage` class constructor takes the following parameters: - -| Parameter | Description | | -| -------------- | -------------------------------------------------------------------------------------------------------------- | -------- | -| `receiverID` | `UID` of the user or `GUID` of the group receiving the message | Required | -| `messageText` | The text message | Required | -| `receiverType` | The type of the receiver- `CometChatReceiverType.user` (user) or `CometChatReceiverType.group` (group) | Required | -| type | The type of the message that needs to be sent which in this case can be: `CometChatMessageType.text`\_\_(text) | | - -When a text message is sent successfully, the response will include a `TextMessage` object which includes all information related to the sent message. - -### Set Quoted Message - -To set a quoted message for a message, use the `setQuotedMessageId` and `setQuotedMessage` method of the TextMessage class. This method accepts the ID of the message to be quoted. - - - - -```dart -textMessage.quotedMessageId = 0 -textMessage.quotedMessage = // Pass base message object here that you want to quote. -``` - - - - -Once the text message object is ready, you need to use the `sendMessage()` method to send the text message to the recipient. - - - -```dart -String receiverID = "UID"; - String messageText = "Hello CometChat!"; - String receiverType = CometChatReceiverType.user; - - TextMessage textMessage = - TextMessage(receiverID, messageText, receiverType); - - CometChat.sendMessage( - textMessage, - onSuccess: (TextMessage message) { - print("Message sent successfully: ${message.toString()}"); - }, - onError: (CometChatException e) { - print("Message sending failed with exception: ${e.message}"); - }, - ); -``` - - - -```dart -String receiverID = "GUID"; - String messageText = "Hello CometChat!"; - String receiverType = CometChatReceiverType.group; - - TextMessage textMessage = - TextMessage(receiverID, messageText, receiverType); - - CometChat.sendMessage( - textMessage, - onSuccess: (TextMessage message) { - print("Message sent successfully: ${message.toString()}"); - }, - onError: (CometChatException e) { - print("Message sending failed with exception: ${e.message}"); - }, - ); -``` - - - - - -The `TextMessage` class constructor takes the following parameters: - -| Parameter | Description | | -| -------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------- | -| `receiverID` | `UID` of the user or `GUID` of the group receiving the message | Required | -| `messageText` | The text message | Required | -| `receiverType` | The type of the receiver- `CometChatConstants.RECEIVER_TYPE_USER` (user) or `CometChatConstants.RECEIVER_TYPE_GROUP` (group) | Required | - -When a text message is sent successfully, the response will include a `TextMessage` object which includes all information related to the sent message. - -## Media Message - -*In other words, as a sender, how do I send a media message like photos, videos & files?* - -To send a media message to any user or group, you need to use the `sendMediaMessage()` method and pass a `MediaMessage` object to it. - -### Add Metadata - -To send custom data along with a media message, you can use the `setMetadata` method and pass a `Map` to it. - - - -```dart -Map metadata = {}; -metadata["lattitude"] = "50.6192171633316" ; -metadata["longitude"] = "-72.68182268750002" ; -mediaMessage.metadata = metadata; -``` - - - - - -### Add Caption(Text along with Media Message) - - - -```dart -mediaMessage.caption = "Message Caption"; -``` - - - - - -### Add Tags - -To add a tag to a message you can use the `setTags()` method of the MediaMessage Class. The `setTags()` method accepts a list of tags. - - - -```dart -List tags = []; -tags.add("pinned"); -mediaMessage.tags = tags; -``` - - - - - -There are 2 ways you can send Media Messages using the CometChat SDK: - -1. **By providing the File :** You can directly share the file object while creating an object of the MediaMessage class. When the media message is sent using the sendMediaMessage() method, this file is then uploaded to CometChat servers and the URL of the file is sent in the success response of the sendMediaMessage() function. - - - -```dart -String receiverID; -String messageType = CometChatMessageType.image; -String receiverType = CometChatConversationType.user ; -String filePath = "storage/emulated/0/Download/46.jpg"; -MediaMessage mediaMessage = MediaMessage( -receiverType: receiverType, -type: messageType, -receiverUid: receiverID, -file: filePath); - -await CometChat.sendMediaMessage(mediaMessage, - onSuccess: (MediaMessage message) { - debugPrint("Media message sent successfully:${mediaMessage.metadata}"); - }, onError: (e) { - debugPrint("Media message sending failed with exception: ${e.message}"); - } -); -``` - - - - - -The `MediaMessage` class constructor takes the following parameters: - -| Parameter | Description | | -| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -| receiverId | The UID or GUID of the recipient | Required | -| file | The file object to be sent | Required | -| messageType | The type of the message that needs to be sent which, in this case, can be:
1. `CometChatMessageType.image` (image)
2. `CometChatMessageType.video` (video)
3. `CometChatMessageType.audio` (audio)
4. `CometChatMessageType.file` (file) | Required | -| receiverType | The type of the receiver to whom the message is to be sent, i.e., `CometChatReceiverType.user` (user) or `CometChatReceiverType.group` (group) | Required | - -2. **By providing the URL of the File:** The second way to send media messages using the CometChat SDK is to provide the SDK with the URL of any file that is hosted on your servers or any cloud storage. To achieve this you will have to make use of the Attachment class that is available in the MediaMessage class. For more information, you can refer to the below code snippet: - - - -```dart -MediaMessage mediaMessage = MediaMessage( - receiverType: receiverType, - type: messageType, - receiverUid: receiverID, - file: null); - -String fileUrl = "https://pngimg.com/uploads/mario/mario_PNG125.png"; -String fileName = "test"; -String fileExtension = "png"; -String fileMimeType = "image/png"; - -Attachment attach = Attachment(fileUrl,fileName,fileExtension,fileMimeType,null); -mediaMessage.attachment= attach; - -await CometChat.sendMediaMessage(mediaMessage, - onSuccess: (MediaMessage message) { - debugPrint( "Media message sent successfully: ${mediaMessage}"); - }, onError: (CometChatException e) { - debugPrint("Media message sending failed with exception: ${e.message}"); - } -); -``` - - - - - -When a media message is sent successfully, the response will include a `MediaMessage` object which includes all information related to the sent message. - -If you wish to send a caption or some text along with the Media Message, you can use the `caption field` provided by the MediaMessage class. To get and set the caption you can use the `.caption` variable . As with text messages, the metadata field can be used with media messages as well. Any additional information can be passed along with the media message as a `Map`. - -### Send Multiple Media Files - -You can send multiple media files (images, videos, audio, or documents) in a single message. This enables richer and more efficient conversations. - - - -```dart -String receiverID = "cometchat-uid-1"; -String receiverType = CometChatConversationType.user; -String messageType = CometChatMessageType.image; - -// Create a list of file paths -List filePaths = [ - "storage/emulated/0/Download/image1.jpg", - "storage/emulated/0/Download/image2.jpg", - "storage/emulated/0/Download/image3.jpg", -]; - -MediaMessage mediaMessage = MediaMessage( - receiverType: receiverType, - type: messageType, - receiverUid: receiverID, - files: filePaths, // Use 'files' for multiple files -); - -await CometChat.sendMediaMessage(mediaMessage, - onSuccess: (MediaMessage message) { - debugPrint("Multiple media files sent successfully"); - // Access the list of attachments - List? attachments = message.attachments; - for (var attachment in attachments ?? []) { - debugPrint("Attachment URL: ${attachment.fileUrl}"); - } - }, - onError: (CometChatException e) { - debugPrint("Media message sending failed: ${e.message}"); - } -); -``` - - - - - -### File Size and Count Validation - -The SDK automatically validates file size and count when sending media messages. You can configure these limits through Settings in the CometChat Dashboard. - -| Validation | Description | -|------------|-------------| -| File Size | Maximum size per file (configurable in Dashboard) | -| File Count | Maximum number of files per message (configurable in Dashboard) | - -If validation fails, the `onError` callback will be triggered with details about the validation error. - - - -```dart -await CometChat.sendMediaMessage(mediaMessage, - onSuccess: (MediaMessage message) { - debugPrint("Media message sent successfully"); - }, - onError: (CometChatException e) { - // Handle validation errors - if (e.code == "ERR_FILE_SIZE_EXCEEDED") { - debugPrint("File size exceeds the allowed limit"); - } else if (e.code == "ERR_FILE_COUNT_EXCEEDED") { - debugPrint("Too many files in a single message"); - } else { - debugPrint("Error: ${e.message}"); - } - } -); -``` - - - - - -## Custom Message - -*In other words, as a sender, how do I send a custom message like location co-ordinates?* - -CometChat allows you to send custom messages which are neither text nor media messages. - -In order to send a custom message, you need to use the `sendCustomMessage()` method. - -The `sendCustomMessage()` methods takes an object of the `CustomMessage` which can be obtained using the below constructor. - - - -```dart -CustomMessage customMessage = CustomMessage( receiverUid: UID, - type: type, - customData: customData, - receiverType: receiverType, - subType: subType, - ); -``` - - - - - -The above constructor, helps you create a custom message with the message type set to whatever is passed to the constructor and the category set to `custom`. - -The parameters involved are: - -1. `receiverUid` - Unique id of the user or group to which the message is to be sent. -2. `receiverType` - Type of the receiver i.e user or group -3. `customType` - custom message type that you need to set -4. `customData` - The data to be passed as the message in the form of a `Map`. - -You can also use the subType field of the `CustomMessage` class to set a specific type for the custom message. This can be achieved using the `subtype` field. - -### Add Tags - -To add a tag to a message you can assign value in `.tags` variable of the CustomMessage Class. `tags` accepts a list of tags. - - - -```dart -List tags = []; -tags.add("pinned"); -textMessage.tags = tags; -``` - - - - - -Once the object of `CustomMessage` class is ready you can send the custom message using the `sendCustomMessage()` method. - - - -```dart -String UID = "UID"; -String subType = "LOCATION"; -String receiverType = CometChatConversationType.user; -String type = CometChatMessageType.custom; - -Map customData = {}; -customData["latitude"] = "19.0760"; -customData["longitude"] = "72.8777"; - -CustomMessage customMessage = CustomMessage( - receiverUid: UID, - type: type, - customData: customData, - receiverType: receiverType, - subType: subType, -); - -CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { - debugPrint("Custom Message Sent Successfully : $message"); -}, onError: (CometChatException e) { - debugPrint("Custom message sending failed with exception: ${e.message}"); -}); -``` - - - - -```dart -String GUID = "GUID"; -String subType = "LOCATION"; -String receiverType = CometChatConversationType.group; -String type = CometChatMessageType.custom; - -Map customData = {}; -customData["latitude"] = "19.0760"; -customData["longitude"] = "72.8777"; - -CustomMessage customMessage = CustomMessage( - receiverUid: GUID, - type: type, - customData: customData, - receiverType: receiverType, - subType: subType, -); - -CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { - debugPrint("Custom Message Sent Successfully : $message"); -}, onError: (CometChatException e) { - debugPrint("Custom message sending failed with exception: ${e.message}"); -}); -``` - - - - - -The above sample explains how custom messages can be used to share the location with a user. Similarly, you can send custom messages to groups. - -On success, you will receive an object of `CustomMessage` class. - -### Update Conversation - -*How can I decide whether the custom message should update the last message of a conversation?* - -By default, a custom message will update the last message of a conversation. If you wish to not update the last message of the conversation when a custom message is sent, please use `updateConversation` (boolean value) method of the Custom Message. - - - -```dart -String UID = "UID"; -String subType = "LOCATION"; -String receiverType = CometChatConversationType.user; -String type = CometChatMessageType.custom; - -Map customData = {}; -customData["latitude"] = "19.0760"; -customData["longitude"] = "72.8777"; - -CustomMessage customMessage = CustomMessage( - receiverUid: UID, - type: type, - customData: customData, - receiverType: receiverType, - subType: subType, -); -customMessage.updateConversation = false; - -CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { - debugPrint("Custom Message Sent Successfully : $message"); -}, onError: (CometChatException e) { - debugPrint("Custom message sending failed with exception: ${e.message}"); -}); -``` - - - - -```dart -String GUID = "GUID"; -String subType = "LOCATION"; -String receiverType = CometChatConversationType.group; -String type = CometChatMessageType.custom; - -Map customData = {}; -customData["latitude"] = "19.0760"; -customData["longitude"] = "72.8777"; - -CustomMessage customMessage = CustomMessage( - receiverUid: GUID, - type: type, - customData: customData, - receiverType: receiverType, - subType: subType, -); -customMessage.updateConversation = false; - -CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { - debugPrint("Custom Message Sent Successfully : $message"); -}, onError: (CometChatException e) { - debugPrint("Custom message sending failed with exception: ${e.message}"); -}); -``` - - - - - -### Custom Notification Body - -*How can i customise the notification body of custom message?* - -To add a custom notification body for `Push, Email & SMS` notification of a custom message you can use the `conversationText` method of Custom Message class. - - - -```dart -String UID = "UID"; -String subType = "LOCATION"; -String receiverType = CometChatConversationType.user; -String type = CometChatMessageType.custom; - -Map customData = {}; -customData["latitude"] = "19.0760"; -customData["longitude"] = "72.8777"; - -CustomMessage customMessage = CustomMessage( - receiverUid: UID, - type: type, - customData: customData, - receiverType: receiverType, - subType: subType, -); -customMessage.conversationText = "Custom Notification Body"; - -CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { - debugPrint("Custom Message Sent Successfully : $message"); -}, onError: (CometChatException e) { - debugPrint("Custom message sending failed with exception: ${e.message}"); -}); -``` - - - - -```dart -String GUID = "GUID"; -String subType = "LOCATION"; -String receiverType = CometChatConversationType.group; -String type = CometChatMessageType.custom; - -Map customData = {}; -customData["latitude"] = "19.0760"; -customData["longitude"] = "72.8777"; - -CustomMessage customMessage = CustomMessage( - receiverUid: GUID, - type: type, - customData: customData, - receiverType: receiverType, - subType: subType, -); -customMessage.conversationText = "Custom Notification Body"; - -CometChat.sendCustomMessage(customMessage, onSuccess: (CustomMessage message) { - debugPrint("Custom Message Sent Successfully : $message"); -}, onError: (CometChatException e) { - debugPrint("Custom message sending failed with exception: ${e.message}"); -}); -``` - - - - - - - -It is also possible to send interactive messages from CometChat , to know more [click here](/sdk/flutter/interactive-messages) - - diff --git a/sdk/flutter/v5/setup.mdx b/sdk/flutter/v5/setup.mdx deleted file mode 100644 index 62827343b..000000000 --- a/sdk/flutter/v5/setup.mdx +++ /dev/null @@ -1,173 +0,0 @@ ---- -title: "Setup" ---- - - -This is a **beta release** of the CometChat Flutter Chat SDK v5. APIs and features may change before the stable release. - - -### Get your Application Keys - -[Signup for CometChat](https://app.cometchat.com/) and then: - -1. Create a new app -2. Head over to the **API Keys** section and note the **Auth Key**, **App ID** & **Region** - - -Minimum Requirement - -* Android API Level 21 -* AndroidX Compatibility -* iOS 11 or higher -* Flutter SDK 1.2 or higher - - - -### Add the CometChat Dependency - -### Cloudsmith (Current Beta) - -For the current beta release, add the Cloudsmith hosted repository and dependency to your `pubspec.yaml`: - -```yaml -dependencies: - cometchat_sdk: - hosted: - url: https://dart.cloudsmith.io/cometchat/cometchat/ - version: 5.0.0-beta.1 -``` - -Then run: -```bash -flutter pub get -``` - - -**Upgrading from v4?** - -If you're migrating an existing app from CometChat SDK v4, check out the [Upgrading from v4](/sdk/flutter/v5/upgrading-from-v4-guide) guide for breaking changes, deprecated methods, and migration instructions. - - -2. Add following code to podfile inside iOS section of your app - - - -```ruby -post_install do |installer| - -installer.pods_project.targets.each do |target| - -flutter_additional_ios_build_settings(target) - -//Copy from here-------> - -target.build_configurations.each do |build_configuration| - -build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' - -end - -//Copy TILL here-------> - -end - -end -``` - - - - - - -**Apple Silicon (M1/M2/M3) users:** Excluding `arm64` from the simulator build prevents the app from running natively on Apple Silicon Macs. If you are developing on an Apple Silicon Mac, consider excluding only `i386` instead of `arm64 i386` to enable native simulator builds. - - -3. For iOS, change the deployment target to `11` or higher. -4. For iOS, navigate to your iOS folder in terminal or CMD and do `pod install` . For apple chip system use rosetta terminal. -5. For iOS you can set the Enabled Bitcode settings to **NO** present in build settings of XCODE project - - - - - -Import CometChat SDK using following code in dart - - - -```dart -import 'package:cometchat_sdk/cometchat_sdk.dart'; -``` - - - - - -## Initialise CometChat - -The `init()` method initialises the settings required for CometChat. The `init()` method takes the below parameters: - -1. appID - You CometChat App ID -2. appSettings - An object of the AppSettings class can be created using the AppSettingsBuilder class. The region field is mandatory and can be set using the `setRegion()` method. - -The `AppSettings` class allows you to configure three settings: - -* **Region**: The region where you app was created. -* **[Presence Subscription](/sdk/flutter/user-presence):** Represents the subscription type for user presence (real-time online/offline status) -* **autoEstablishSocketConnection(boolean value)**: This property takes a boolean value which when set to true informs the SDK to manage the web-socket connection internally. If set to false, it informs the SDK that the web-socket connection will be managed manually. The default value for this parameter is true. For more information on this, please check the [Connection Behaviour](/sdk/flutter/connection-behaviour) section. The default value for this property is **true.** -* **adminHost(adminHost: string)**: This method takes the admin URL as input and uses this admin URL instead of the default admin URL. This can be used in case of dedicated deployment of CometChat. -* **clientHost(clientHost: string)**: This method takes the client URL as input and uses this client URL instead of the default client URL. This can be used in case of dedicated deployment of CometChat. - - - -```dart -String region = "REGION"; -String appId = "APP_ID"; - -AppSettings appSettings= (AppSettingsBuilder() - ..subscriptionType = CometChatSubscriptionType.allUsers - ..region= region - ..adminHost = "" //optional - ..clientHost = "" //optional - ..autoEstablishSocketConnection = true -).build(); - -CometChat.init(appId, appSettings, - onSuccess: (String successMessage) { - debugPrint("Initialization completed successfully $successMessage"); - }, onError: (CometChatException excep) { - debugPrint("Initialization failed with exception: ${excep.message}"); - } -); -``` - - - - - -We suggest you call the `init()` method on app startup. - -## Auto Mode Connection - - - -Know more about auto mode connection [click here](/sdk/flutter/connection-behaviour) - - - -| App State | Behaviour | -| ----------------- | --------------------------------------- | -| App in foreground | Connected with WebSocket | -| App in background | Immediately disconnected with WebSocket | - -## Manual Mode Connection - - - -Know more about manual mode connection [click here](/sdk/flutter/connection-behaviour) - - - -| App State | Behaviour | -| ----------------- | ------------------------------------------------------------------------------------------------------------------ | -| App in foreground | Call `CometChat.connect()` to create the WebSocket connection | -| App in background | Disconnect the WebSocket connection if no ping is received within 30 seconds after the app goes in the background. | diff --git a/sdk/flutter/v5/threaded-messages.mdx b/sdk/flutter/v5/threaded-messages.mdx deleted file mode 100644 index 330506158..000000000 --- a/sdk/flutter/v5/threaded-messages.mdx +++ /dev/null @@ -1,150 +0,0 @@ ---- -title: "Threaded Messages" ---- - - - -Messages that are started from a particular message are called Threaded messages or simply threads. -Each Thread is attached to a message which is the Parent message for that thread. - -## Send Message in a Thread - -As mentioned in the [Send a Message](send-message) section, you can send a message to a User or a Group by mentioning the receiver (uid/guid) and `receiverType`(user/group). - -A message can be categorized as: - -1. Text Message -2. Media Message -3. Custom Message. -4. Interactive Message - -Any of the above messages can be sent in a thread. As a thread is identified with a parent message, the `parentMessageId` must be set for the message. This will indicate that the message to be sent has to be a part of the thread of the message with the specified `parentMessageId`. - -This can be achieved using the `parentMessageId` parameter provided by the object of the `TextMessage`, `MediaMessage` and `CustomMessage` class. The id specified in the `parentMessageId` parameter maps the message sent to the particular thread. - -**Example to Send a Text Message in a thread in a user conversation.** - - - -```dart -String receiverID = "UID"; -String messagesText = "Hello"; -String receiverType = CometChatConversationType.user; -String type = CometChatMessageType.text; - -TextMessage textMessage = TextMessage( - text: messagesText, - receiverUid: receiverID, - receiverType: receiverType, - type: type); -textMessage.parentMessageId = 103 - -CometChat.sendMessage(textMessage,onSuccess: (TextMessage message) { -debugPrint("Message sent successfully: $message"); -}, onError: (CometChatException e) { -debugPrint("Message sending failed with exception: ${e.message}"); -}); -``` - - - - -The above snippet shows how a message with the text "Hello" can be sent in the thread with `parentMessageId` 103. - -Similarly, using the `parentMessageId` parameter, Media and Custom Messages can be sent in threads too. - -### Receiving Real-Time Messages - -The procedure to receive real-time messages is exactly the same as mentioned in the [Receive Messages](receive-messages). This can be achieved using the `MessageListener` class provided by the SDK. -To add a MessageListener, you can use the `addMessageListener()` method of the SDK. -The only thing that needs to be checked is if the received message belongs to the active thread. This can be done using `parentMessageId` field of the message object. - - - -```dart -class Class_Name with MessageListener { - int activeParentMessageId= 103; -//CometChat.addMessageListener("listenerId", this); - @override -void onTextMessageReceived(TextMessage textMessage) { - if(textMessage.parentMessageId == activeParentMessageId){ - debugPrint("Text message received successfully: $textMessage"); - } - -} - -@override -void onMediaMessageReceived(MediaMessage mediaMessage) { - if(mediaMessage.parentMessageId== activeParentMessageId){ - debugPrint("Media message received successfully: $mediaMessage"); - } -} - -@override -void onCustomMessageReceived(CustomMessage customMessage) { - if(customMessage.parentMessageId== activeParentMessageId){ - debugPrint("Custom message received successfully: $customMessage"); - } -} -} -``` - - - - -### Fetch all the messages for any particular thread. - -You can fetch all the messages belonging to a particular thread by using the `MessagesRequest` class. - -The `MessageRequestBuilder` builds the `MessageRequest` object using the following functions: - -1. parentMessageId: Takes the parentId of the message as argument whose thread needs to be requested. -2. build(): returns the MessageRequest object. - -Once you have the `MessagesRequest` object, you can call the `fetchPrevious()` method to get the latest messages in the thread. In one iteration, a maximum of 100 messages can be fetched. If you wish to fetch the next set of messages, you need to call the fetchPrevious() method again on the same object. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..parentMessageId = 103 - ..limit = 50).build(); - -messageRequest.fetchPrevious(onSuccess: (List list) { - debugPrint("Message fetching Successful"); - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - -## Avoid Threaded Messages in User/Group Conversations - -While fetching messages for normal user/group conversations using the `MessagesRequest`, the threaded messages by default will be a part of the list of messages received. In order to exclude the threaded messages from the list of user/group messages, you need to use the `hideReplies` parameter of the `MessagesRequestBuilder` class. This method takes a boolean argument which when set to true excludes the messages belonging to threads from the list of messages. - - - -```dart -String UID = "cometchat-uid-1"; - -MessagesRequest messageRequest = (MessagesRequestBuilder() - ..uid = UID - ..hideReplies = true - ..limit = 50).build(); - -messageRequest.fetchNext(onSuccess: (List list) { - debugPrint("Message fetching Successful"); - }, onError: (CometChatException e) { - debugPrint("Message fetching failed with exception: ${e.message}"); - }); -``` - - - - -The above snippet will return messages between the logged in user and `cometchat-uid-1` excluding all the threaded messages belonging to the same conversation. \ No newline at end of file diff --git a/sdk/flutter/v5/transfer-group-ownership.mdx b/sdk/flutter/v5/transfer-group-ownership.mdx deleted file mode 100644 index 5c2b8b668..000000000 --- a/sdk/flutter/v5/transfer-group-ownership.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: "Transfer Group Ownership" ---- - - - -*In other words, as a logged-in user, how do I transfer the ownership of any group if I am the owner of the group?* - -In order to transfer the ownership of any group, the first condition is that you must be the owner of the group. In case you are the owner of the group, you can use the `transferGroupOwnership()` method provided by the `CometChat` class. - -This will be helpful as the owner is not allowed to leave the group. In case, you as the owner would like to leave the group, you will have to use this method and transfer your ownership first to any other member of the group and only then you will be allowed to leave the group. - - - -```dart -String UID = "cometchat-uid-1"; -String GUID = conversationWith; -CometChat.transferGroupOwnership(guid: GUID,uid: UID, - onSuccess: (String message) { - debugPrint("Owner Transferred Successfully : $message"); - }, - onError: (CometChatException e) { - debugPrint("Owner Transferred failed : ${e.message}"); - }); -``` - - - - diff --git a/sdk/flutter/v5/transient-messages.mdx b/sdk/flutter/v5/transient-messages.mdx deleted file mode 100644 index e39b5e46f..000000000 --- a/sdk/flutter/v5/transient-messages.mdx +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: "Transient Messages" ---- - - - -Transient messages are messages that are sent in real-time only and are not saved or tracked anywhere. The receiver of the message will only receive the message if he is online and these messages cannot be retrieved later. - -## Send a Transient Message - -You can use the `sendTransientMessage()` method to send a transient message to a user or in a group. The receiver will receive this information in the `onTransientMessageReceived()` method of the `MessageListener` class. In order to send the transient message, you need to use the `TransientMessage` class. - - - -```dart -String receiverId = "cometchat-uid-2"; -Map data= {}; -data["LIVE_REACTION"] = "heart"; - -TransientMessage transientMessage = TransientMessage( receiverId:receiverId , receiverType: CometChatReceiverType.user , data: data, ); - -CometChat.sendTransientMessage(transientMessage, onSuccess: (){ - debugPrint("Transient Message Sent"); - }, onError: (CometChatException e){ - debugPrint("Transient message sending failed with exception: ${e.message}"); -}); -``` - - - - - -## Real-time Transient Messages - -*In other words, as a recipient, how do I know when someone sends a transient message?* - -You will receive the transient message in the `onTransientMessageReceived()` method of the registered `MessageListener` class. - - - -```dart -class Class_Name with MessageListener { - -//CometChat.addMessageListener("listenerId", this); -@override -void onTransientMessageReceived(TransientMessage message) { - // TODO: implement onTransientMessageReceived -} - - -} -``` - - - - - -The `TransientMessage` class consists of the below parameters: - -| Parameter | Information | -| ---------------- | --------------------------------------------------------------------------------------------------------- | -| **sender** | An object of the User class holding all the information. related to the sender of the transient message. | -| **receiverId** | Unique Id of the receiver. This can be the Id of the group or the user the transient message is sent to. | -| **receiverType** | The type of the receiver - `CometChatReceiverType.user` (user) or `CometChatReceiverType.``group `(group) | -| **data** | A Map to provide data. | diff --git a/sdk/flutter/v5/typing-indicators.mdx b/sdk/flutter/v5/typing-indicators.mdx deleted file mode 100644 index c3787c105..000000000 --- a/sdk/flutter/v5/typing-indicators.mdx +++ /dev/null @@ -1,107 +0,0 @@ ---- -title: "Typing Indicators" ---- - - - -## Send a Typing Indicator - -*In other words, as a sender, how do I let the recipient(s) know that I'm typing?* - -### Start Typing - -You can use the `startTyping()` method to inform the receiver that the logged in user has started typing. The receiver will receive this information in the `onTypingStarted()` method of the `MessageListener` class. In order to send the typing indicator, you need to use the `TypingIndicator` class. - - - -```dart -CometChat.startTyping( - receiverUid: "UID", - receiverType: CometChatReceiverType.user, - ); -``` - - - - -```dart -CometChat.startTyping( - receiverUid: "GUID", - receiverType: CometChatReceiverType.group, - ); -``` - - - - - -### Stop Typing - -You can use the `endTyping()` method to inform the receiver that the logged in user has stopped typing. The receiver will receive this information in the `onTypingEnded()` method of the `MessageListener` class. In order to send the typing indicator, you need to use the `TypingIndicator` class. - - - -```dart -CometChat.endTyping( - receiverUid: "UID", - receiverType: CometChatReceiverType.user); -``` - - - - -```dart -CometChat.endTyping( - receiverUid: "GUID", - receiverType: CometChatReceiverType.group); -``` - - - - - - -Custom Data - -You can use the `metadata` field of the `TypingIndicator` class to pass additional data along with the typing indicators. The metadata field is a Map and can be set using the `.metadata` parameter of the `TypingIndicator` class. This data will be received at the receiver end and can be obtained using the same parameter. - - - -## Real-time Typing Indicators - -*In other words, as a recipient, how do I know when someone is typing?* - -You will receive the typing indicators in the `onTypingStarted()` and the `onTypingEnded()` method of the registered `MessageListener` class. - - - -```dart -class Class_Name with MessageListener { - -//CometChat.addMessageListener("listenerId", this); -@override -void onTypingStarted(TypingIndicator typingIndicator) { - // TODO: implement onTypingStarted -} - -@override -void onTypingEnded(TypingIndicator typingIndicator) { - // TODO: implement onTypingEnded -} - - -} -``` - - - - - -The `TypingIndicator` class consists of the below parameters: - -| Parameter | Information | -| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `sender` | An object of the `User` class holding all the information related to the sender of the typing indicator. | -| `receiverId` | `UID` of the receiver. This is the ID of the group or the user the typing indicator is being sent to. | -| `receiverType` | This parameter indicates if the typing indicator is to be sent to a user or a group. The possible values are: 1. `CometChatConstants.RECEIVER_TYPE_USER` 2. `CometChatConstants.RECEIVER_TYPE_GROUP` | -| `metadata` | A JSONObject to provider additional data | diff --git a/sdk/flutter/v5/update-group.mdx b/sdk/flutter/v5/update-group.mdx deleted file mode 100644 index c9353f983..000000000 --- a/sdk/flutter/v5/update-group.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: "Update A Group" ---- - - - -## Update Group - -*In other words, as a group owner, how can I update the group details?* - -You can update the existing details of the group using the `updateGroup()` method. - - - -```dart -String GUID = "GUID"; - String groupName = "Hello Group!"; - String groupType = CometChatGroupType.public; - String password = ""; - - Group _group = Group(guid: GUID, name: groupName, type: groupType); - - - await CometChat.updateGroup(group: _group, onSuccess: (Group group ){ - debugPrint("Group Created Successfully : $group "); - }, onError:(CometChatException e) { - debugPrint("Group Creation failed with exception: ${e.message}"); - } ); -``` - - - - - -This method takes an instance of the `Group` class as a parameter which should contain the data that you wish to update. - -| Parameter | Description | -| --------- | ---------------------------- | -| `group` | an instance of class `Group` | - -After the successful update of the group, you will receive an instance of `Group` class containing updated information of the group. - -For more information on the `Group` class, please check [here](/sdk/flutter/create-group#group-class) diff --git a/sdk/flutter/v5/user-management.mdx b/sdk/flutter/v5/user-management.mdx deleted file mode 100644 index b58d3da36..000000000 --- a/sdk/flutter/v5/user-management.mdx +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: "User Management" ---- - - - -When a user logs into your app, you need to programmatically login the user into CometChat. But before you log in the user to CometChat, you need to create the user. - -Summing up- - -**When a user registers in your app** - -1. You add the user details in your database -2. You create a user in CometChat - -**When a user logs into your app** - -1. You log in the user to your app -2. You [log in the user to CometChat](https://app.cometchat.com/login) - -## Creating a user - -Ideally, user creation should take place at your backend. You can refer our Rest API to learn more about [creating a user](https://api-explorer.cometchat.com/reference/create-user) and use the appropriate code sample based on your backend language. - -However, if you wish to create users on the fly, you can use the `createUser()` method. This method takes a `User` object and the `API Key` as input parameters and returns the created `User` object if the request is successful. - -For more details on the fields present in the User class, please check [this](/sdk/flutter/user-management#user-class) - - - -```dart -String authKey = "AUTH_KEY";//Replace with the auth key of app -User user = User( uid: "usr1" , name: "Kevin" );//Replace with name and uid of user - -CometChat.createUser(user, authKey, onSuccess: (User user){ - debugPrint("Create User succesful ${user}"); - - }, onError: (CometChatException e){ - debugPrint("Create User Failed with exception ${e.message}"); - - }); -``` - - - - - - - -UID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. - - - -## Updating a user - -Updating a user similar to creating a user should ideally be achieved at your backend using the Restful APIs. For more information, you can check the [update a user](https://api-explorer.cometchat.com/reference/update-user) section. However, this can be achieved on the fly as well using the `updateUser()` method. This method takes a `User` object and the API Key as inputs and returns the updated `User` object on successful execution of the request. - -Please make sure the `User` object provided to the `updateUser()` method has the `UID` of the user to be updated set. - -## Updating logged-in user - -Updating a logged-in user is similar to updating a user. The only difference being this method does not require an AuthKey. This method takes a `User` object as input and returns the updated `User` object on the successful execution of the request. - - - -```dart -User user = User(name: 'Updated Name'); - - -CometChat.updateCurrentUserDetails(user, onSuccess: (User updatedUser) { - debugPrint("Updated User: $updatedUser"); - }, onError: (CometChatException e) { - debugPrint("Updated User exception : ${e.message}"); - }); -``` - - - - - -By using the `updateCurrentUserDetails()` method one can only update the logged-in user irrespective of the UID passed. Also, it is not possible to update the role of a logged-in user. - -## Deleting a user - -Deleting a user can only be achieved via the Restful APIs. For more information please check the [delete a user](https://api-explorer.cometchat.com/reference/delete-user)section. - -## User Class - -| Field | Editable | Information | -| ------------- | --------------------------------------------------- | -------------------------------------------------------------------- | -| uid | specified on user creation. Not editable after that | Unique identifier of the user | -| name | Yes | Display name of the user | -| avatar | Yes | URL to profile picture of the user | -| link | Yes | URL to profile page | -| role | Yes | User role of the user for role based access control | -| metadata | Yes | Additional information about the user as JSON | -| status | No | Status of the user. Could be either online/offline | -| statusMessage | Yes | Any custom status message that needs to be set for a user | -| lastActiveAt | No | The unix timestamp of the time the user was last active. | -| hasBlockedMe | No | A boolean that determines if the user has blocked the logged in user | -| blockedByMe | No | A boolean that determines if the logged in user has blocked the user | -| tags | Yes | A list of tags to identify specific users | diff --git a/sdk/flutter/v5/user-presence.mdx b/sdk/flutter/v5/user-presence.mdx deleted file mode 100644 index a9721a3d9..000000000 --- a/sdk/flutter/v5/user-presence.mdx +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: "User Presence" ---- - - - -User Presence helps us understand if a user is available to chat or not. - -## Real-time Presence - -*In other words, as a logged-in user, how do I know if a user is online or offline?* - -Based on the settings provided in the `AppSettings` class while initializing CometChat using the `init()` method, the logged-in user will receive the presence for the other users in the app. - -In the `AppSettings` class, you can set the type of presence you wish to receive. - -For presence subscription, the `AppSettingsBuilder` provides 3 methods : - -* `subscribePresenceForAllUsers()` - This will inform the logged-in user when any user in the app comes online or goes offline. -* `subscribePresenceForRoles(List roles)` - This will inform the logged-in user, only when the users with the specified roles come online or go offline. -* `subscribePresenceForFriends()` - This will inform the logged-in user when any of their friends come online or go offline. - -If none of the above methods are set, no presence will be sent to the logged-in user. - -For every activity or fragment you wish to receive user events in, you need to register the `UserListener` using the `addUserListener()` method. - -We suggest adding the listener in the `init` method of the activity or the fragment where you wish to receive these events in. - - - -```dart -class Class_Name with UserListener { - -//CometChat.addUserListener("user_Listener_id", this); - @override -void onUserOnline(User user) { - // TODO: implement onUserOnline -} -@override -void onUserOffline(User user) { - // TODO: implement onUserOffline -} - - -} -``` - - - - - -| Parameter | Description | -| ------------ | ----------------------------------------------------------------------------------------------- | -| `listenerID` | An ID that uniquely identifies that listener. We recommend using the activity or fragment name. | - -You will receive an object of the `User` class in the listener methods. - -We recommend you remove the listener once the listener is not in use. - - - -```dart -String listenerID = "UNIQUE_LISTENER_ID"; - -CometChat.removeUserListener(listenerID); -``` - - - - - -## User List Presence - -*In other words, as a logged-in user, when I retrieve the user list, how do I know if a user is online/offline?* - -When you [retrieve the list of users](/sdk/flutter/retrieve-users) , in the[User](/sdk/flutter/user-management#user-class) object, you will receive 2 keys: - -1. `status` - This will hold either of the two values : - -* `online` - This indicates that the user is currently online and available to chat. -* `offline` - This indicates that the user is currently offline and is not available to chat. - -2. `lastActiveAt` - In case the user is offline, this field holds the timestamp of the time when the user was last online. This can be used to display a **Last seen** for that user. diff --git a/sdk/flutter/v5/users-overview.mdx b/sdk/flutter/v5/users-overview.mdx deleted file mode 100644 index 8dcfcd91e..000000000 --- a/sdk/flutter/v5/users-overview.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: "Users" -sidebarTitle: "Overview" ---- - -A user is anyone who uses CometChat. The primary aim for the users functionality is to allow you to quickly retrieve and add users to CometChat. - -## UID - -- Each user is uniquely identified using a UID. -- The UID is typically the primary ID of the user from your database. - - -UID can be alphanumeric with underscore and hyphen. Spaces, punctuation and other special characters are not allowed. - - -## Auth Token - -- A single user can have multiple auth tokens. Auth tokens should be per user per device. -- It should be generated via a server-to-server API call. The auth token should then be passed to CometChat for login. -- An Auth Token can only be deleted via the dashboard or using the REST API. - -## User Roles - -A role is a category for a group of similar users. For example, you may want to group your premium users using the role "Premium". You then use this to filter users or enable/disable features by writing conditional code. - -## User List - -- The User List can be used to build the **Contacts** or **Who's Online** view in your app. -- The list of users can be different based on the logged-in user. - -## Next Steps - -- [User Management](/sdk/flutter/v5/user-management) — sync your users to CometChat -- [Retrieve Users](/sdk/flutter/v5/retrieve-users) — fetch and display users in your app diff --git a/sdk/flutter/v5/webhooks-overview.mdx b/sdk/flutter/v5/webhooks-overview.mdx deleted file mode 100644 index 6c9724091..000000000 --- a/sdk/flutter/v5/webhooks-overview.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Webhooks" -url: "/fundamentals/webhooks" ---- \ No newline at end of file diff --git a/sdk/flutter/webhooks-overview.mdx b/sdk/flutter/webhooks-overview.mdx index d52007a59..6c9724091 100644 --- a/sdk/flutter/webhooks-overview.mdx +++ b/sdk/flutter/webhooks-overview.mdx @@ -1,115 +1,4 @@ --- title: "Webhooks" -sidebarTitle: "Webhooks" -description: "Configure server-side webhooks to receive real-time notifications for messages, users, groups, calls, and moderation events in your Flutter application." url: "/fundamentals/webhooks" ---- - - - -Webhooks send HTTP POST requests to your server when events occur: - -**Setup Requirements:** -- HTTPS endpoint (SSL required) -- Publicly accessible URL -- Support POST method with `application/json` -- Return HTTP 200 OK to acknowledge receipt - -**Event Categories:** -- **Messages:** `message_sent`, `message_edited`, `message_deleted`, `message_read_receipt` -- **Users:** `user_blocked`, `user_unblocked`, `user_connection_status_changed` -- **Groups:** `group_created`, `group_member_added`, `group_member_left` -- **Calls:** `call_initiated`, `call_started`, `call_ended`, `recording_generated` -- **Moderation:** `moderation_engine_approved`, `moderation_engine_blocked` - -**Configure via:** [CometChat Dashboard](https://app.cometchat.com) → Your App → Webhooks - - -CometChat Webhooks enable real-time, event-driven communication with your server by sending HTTP POST requests for specific events such as messages, user actions, group updates, calls, and moderation results. - -You can use webhooks to build custom workflows such as sending SMS or email notifications, logging activity, syncing with external systems, or triggering automation. - - -Webhooks are configured at the application level through the CometChat Dashboard, not within the Flutter SDK. The SDK handles real-time events via listeners, while webhooks deliver events to your backend server. - - ---- - -## When to Use Webhooks vs SDK Listeners - -| Use Case | Solution | -| --- | --- | -| Real-time updates in Flutter app | SDK Listeners (`CometChatMessageEvents`, `CometChatUserEvents`) | -| Server-side processing | Webhooks | -| Push notifications to offline users | Webhooks | -| Analytics and logging | Webhooks | -| Third-party integrations | Webhooks | -| Syncing with external databases | Webhooks | - ---- - -## Setting Up Webhooks - -Webhooks are configured through the CometChat Dashboard: - - - - Go to [CometChat Dashboard](https://app.cometchat.com) → Your App → Webhooks - - - Click **Create Webhook** and enter your HTTPS endpoint URL - - - Choose which events should trigger webhook calls (messages, users, groups, calls, moderation) - - - Set up Basic Authentication with username/password for security - - - ---- - -## Webhook Endpoint Requirements - -Your webhook endpoint must meet these criteria: - -1. **Use HTTPS** – All webhook URLs must be secured with SSL -2. **Be publicly accessible** – Your server should be reachable from the internet -3. **Support POST method** – Events are delivered as `HTTP POST` requests with `application/json` content -4. **Return HTTP 200 OK** – Acknowledge receipt within 2 seconds - ---- - -## Best Practices - - - Enable `retryOnFailure` when setting up webhooks. CometChat retries failed deliveries after 10 seconds, then 30 seconds. Use unique event IDs to deduplicate retries. - - - For long processing tasks, enqueue events to systems like Kafka, RabbitMQ, or AWS SQS, and process them asynchronously. Respond within 2 seconds to prevent timeouts. - - - Return appropriate HTTP status codes: `200 OK` for success, `4xx` for client errors, `5xx` for server issues. - - - Maintain detailed logs of incoming webhook requests and responses. Track failures, latency, and retry attempts. - - ---- - -## Next Steps - - - - Handle events directly in your Flutter app with SDK listeners - - - Explore additional SDK resources and documentation - - - View all webhook event payloads and details - - - Learn how to create and manage webhooks via REST API - - +--- \ No newline at end of file From bfea85e98b38940939f7e26267a9e09d8d7ba31c Mon Sep 17 00:00:00 2001 From: Anshuman Pathania Date: Thu, 7 May 2026 19:33:01 +0530 Subject: [PATCH 03/61] fix: remove empty beta warning banners and fix /v5/ internal links - Remove empty tags from overview.mdx and setup.mdx - Replace all /sdk/flutter/v5/ links with /sdk/flutter/ (4 files) Committed-by: kiro-bot --- sdk/flutter/additional-message-filtering.mdx | 4 +-- sdk/flutter/messaging-overview.mdx | 6 ++--- sdk/flutter/overview.mdx | 27 +++++++++----------- sdk/flutter/setup.mdx | 5 +--- sdk/flutter/users-overview.mdx | 4 +-- 5 files changed, 20 insertions(+), 26 deletions(-) diff --git a/sdk/flutter/additional-message-filtering.mdx b/sdk/flutter/additional-message-filtering.mdx index 1d9687d68..5ce56ff1b 100644 --- a/sdk/flutter/additional-message-filtering.mdx +++ b/sdk/flutter/additional-message-filtering.mdx @@ -245,7 +245,7 @@ MessagesRequest messageRequest = (MessagesRequestBuilder() *In other words, how do I fetch messages belonging to multiple categories* -We recommend before trying this, you refer to the [Message Structure and Hierarchy](/sdk/flutter/v5/messaging-overview#message-structure-and-hierarchy) guide to get familiar with the various categories of messages. +We recommend before trying this, you refer to the [Message Structure and Hierarchy](/sdk/flutter/messaging-overview#message-structure-and-hierarchy) guide to get familiar with the various categories of messages. For this, you will have to use the `categories` method. This method accepts a list of categories. This tells the SDK to fetch messages only belonging to these categories. @@ -273,7 +273,7 @@ The above snippet will help you get only the messages belonging to the `message` *In other words, how do I fetch messages belonging to multiple types* -We recommend you refer to the [Message Structure and Hierarchy](/sdk/flutter/v5/messaging-overview#message-structure-and-hierarchy) guide to get familiar with the various types of messages before trying this out. +We recommend you refer to the [Message Structure and Hierarchy](/sdk/flutter/messaging-overview#message-structure-and-hierarchy) guide to get familiar with the various types of messages before trying this out. This can be easily achieved using the `types` parameter, which accepts a list of types. This tells the SDK to fetch messages only belonging to these types. diff --git a/sdk/flutter/messaging-overview.mdx b/sdk/flutter/messaging-overview.mdx index 71c3a8d4b..4d3acd3e9 100644 --- a/sdk/flutter/messaging-overview.mdx +++ b/sdk/flutter/messaging-overview.mdx @@ -5,9 +5,9 @@ sidebarTitle: "Overview" Messaging is one of the core features of CometChat. We've thoughtfully created methods to help you send, receive, and fetch message history. -At the minimum, you must add code for [sending messages](/sdk/flutter/v5/send-message) and [receiving messages](/sdk/flutter/v5/receive-messages). +At the minimum, you must add code for [sending messages](/sdk/flutter/send-message) and [receiving messages](/sdk/flutter/receive-messages). -Once you've implemented that, you can proceed to more advanced features like [typing indicators](/sdk/flutter/v5/typing-indicators) and [delivery & read receipts](/sdk/flutter/v5/delivery-read-receipts). +Once you've implemented that, you can proceed to more advanced features like [typing indicators](/sdk/flutter/typing-indicators) and [delivery & read receipts](/sdk/flutter/delivery-read-receipts). ## Message Structure and Hierarchy @@ -70,7 +70,7 @@ An `InteractiveMessage` encapsulates an interactive unit within a chat message, 3. **scheduler** — scheduler message 4. **customInteractive** — custom interaction messages -See [Interactive Messages](/sdk/flutter/v5/interactive-messages) for more details. +See [Interactive Messages](/sdk/flutter/interactive-messages) for more details. ### Action diff --git a/sdk/flutter/overview.mdx b/sdk/flutter/overview.mdx index d2f57628e..5dcc533ec 100644 --- a/sdk/flutter/overview.mdx +++ b/sdk/flutter/overview.mdx @@ -5,9 +5,6 @@ sidebarTitle: "Overview" The CometChat Chat SDK for Flutter enables real-time messaging, user management, group conversations, and more in your Flutter application. Built as a pure Dart implementation in v5, it removes the dependency on platform channels and works seamlessly across Android, iOS, Web, and desktop. - - - **Faster Integration with UI Kits** @@ -24,10 +21,10 @@ Use this Chat SDK directly only if you need custom UI or advanced control. **Upgrading from v4?** -If you're migrating an existing app from CometChat SDK v4, check out the [Upgrading from v4](/sdk/flutter/v5/upgrading-from-v4-guide) guide for breaking changes, deprecated methods, and migration instructions. +If you're migrating an existing app from CometChat SDK v4, check out the [Upgrading from v4](/sdk/flutter/upgrading-from-v4-guide) guide for breaking changes, deprecated methods, and migration instructions. -Before integrating the Chat SDK, ensure you have a [CometChat Account](https://app.cometchat.com/signup) with your App ID, Region, and Auth Key. Flutter SDK `>=1.2` is required, with Android API Level 21+ and iOS 11+. Users must exist in CometChat to send or receive messages — see [Authentication](/sdk/flutter/v5/authentication-overview) for details. +Before integrating the Chat SDK, ensure you have a [CometChat Account](https://app.cometchat.com/signup) with your App ID, Region, and Auth Key. Flutter SDK `>=1.2` is required, with Android API Level 21+ and iOS 11+. Users must exist in CometChat to send or receive messages — see [Authentication](/sdk/flutter/authentication-overview) for details. ```mermaid sequenceDiagram @@ -44,43 +41,43 @@ sequenceDiagram - + Send text, media, and custom messages in 1-on-1 or group conversations - + Create, join, and manage group conversations with member roles and scopes - + Listen for messages, typing indicators, read receipts, and presence changes in real time - + Show real-time typing status for users and groups - + Track online/offline status of users with configurable subscription modes - + Add and manage emoji reactions on messages - + Organize conversations with message threads - + Track message delivery and read status - + Integrate AI-powered agents into your chat experience - + Voice and video calling with built-in UI components diff --git a/sdk/flutter/setup.mdx b/sdk/flutter/setup.mdx index e01fa4627..14e1fa426 100644 --- a/sdk/flutter/setup.mdx +++ b/sdk/flutter/setup.mdx @@ -2,9 +2,6 @@ title: "Setup" --- - - - ### Get your Application Keys [Signup for CometChat](https://app.cometchat.com/) and then: @@ -44,7 +41,7 @@ flutter pub get **Upgrading from v4?** -If you're migrating an existing app from CometChat SDK v4, check out the [Upgrading from v4](/sdk/flutter/v5/upgrading-from-v4-guide) guide for breaking changes, deprecated methods, and migration instructions. +If you're migrating an existing app from CometChat SDK v4, check out the [Upgrading from v4](/sdk/flutter/upgrading-from-v4-guide) guide for breaking changes, deprecated methods, and migration instructions. 2. Add following code to podfile inside iOS section of your app diff --git a/sdk/flutter/users-overview.mdx b/sdk/flutter/users-overview.mdx index 8dcfcd91e..868ac1bf1 100644 --- a/sdk/flutter/users-overview.mdx +++ b/sdk/flutter/users-overview.mdx @@ -31,5 +31,5 @@ A role is a category for a group of similar users. For example, you may want to ## Next Steps -- [User Management](/sdk/flutter/v5/user-management) — sync your users to CometChat -- [Retrieve Users](/sdk/flutter/v5/retrieve-users) — fetch and display users in your app +- [User Management](/sdk/flutter/user-management) — sync your users to CometChat +- [Retrieve Users](/sdk/flutter/retrieve-users) — fetch and display users in your app From fd46492c42f7c8bbd22374fe925091f3b5a0ccfb Mon Sep 17 00:00:00 2001 From: omkar6750 Date: Thu, 7 May 2026 20:47:50 +0530 Subject: [PATCH 04/61] docs: fix broken links in notifications, tenor, and mastra guides - update Flutter push notifications sample link to app_credentials.dart - replace broken Tenor dashboard link with tenor.com - remove nonexistent workflow links from the Mastra frontend actions guide - repoint backend workflow reference to the orchestrator workflow example - add follow-up notes for Mastra repo/doc gaps - mark the first broken-link CSV pair as completed in link-triage tracking --- .gitignore | 1 + ai-agents/mastra-backend-tools-agent.mdx | 4 ++-- ai-agents/mastra-frontend-actions-agent.mdx | 6 ------ fundamentals/tenor.mdx | 2 +- notifications/flutter-push-notifications-ios.mdx | 2 +- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 681ba2d12..5770ed110 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ __pycache__/ /docs-comparison-tool /node_modules /legacy-docs +AGENTS.md diff --git a/ai-agents/mastra-backend-tools-agent.mdx b/ai-agents/mastra-backend-tools-agent.mdx index 9e7431db5..c80e66359 100644 --- a/ai-agents/mastra-backend-tools-agent.mdx +++ b/ai-agents/mastra-backend-tools-agent.mdx @@ -65,7 +65,7 @@ Core files and folders for the Backend Tools Agent (browse source on GitHub): - Server - [src/mastra/index.ts](https://github.com/cometchat/ai-agent-mastra-examples/blob/main/mastra-backend-tools-agent/src/mastra/index.ts) - Workflows - - [src/mastra/workflows/index.ts](https://github.com/cometchat/ai-agent-mastra-examples/blob/main/mastra-backend-tools-agent/src/mastra/workflows/index.ts) + - [src/mastra/workflows/orchestrator-workflow.ts](https://github.com/cometchat/ai-agent-mastra-examples/blob/main/mastra-orchestrator-agent/src/mastra/workflows/orchestrator-workflow.ts) *** @@ -191,4 +191,4 @@ curl -X POST http://localhost:4111/api/agents/deals/generate \ * Add more backend tools (e.g., get-order, create-ticket) and guard with RBAC. * Stream responses or add partial updates for long-running actions. -* Instrument and log tool invocations for tuning and observability. \ No newline at end of file +* Instrument and log tool invocations for tuning and observability. diff --git a/ai-agents/mastra-frontend-actions-agent.mdx b/ai-agents/mastra-frontend-actions-agent.mdx index f60384477..2c773e97a 100644 --- a/ai-agents/mastra-frontend-actions-agent.mdx +++ b/ai-agents/mastra-frontend-actions-agent.mdx @@ -50,9 +50,6 @@ Give your chats superpowers: let an agent trigger visual effects and UI actions Frontend sample - [widget/index.html](https://github.com/cometchat/ai-agent-mastra-examples/blob/main/mastra-frontend-actions-agent/widget/index.html) - This keeps sensitive work on the server and visual effects on the client. - Workflows - - [src/mastra/workflows/index.ts](https://github.com/cometchat/ai-agent-mastra-examples/blob/main/mastra-frontend-actions-agent/src/mastra/workflows/index.ts) - Key components (source-linked below): the agent, the `confetti` tool, server entry, and a sample widget page. *** @@ -76,9 +73,6 @@ Key components (source-linked below): the agent, the `confetti` tool, server ent - [src/mastra/index.ts](https://github.com/cometchat/ai-agent-mastra-examples/blob/main/mastra-frontend-actions-agent/src/mastra/index.ts) - Frontend sample - [widget/index.html](https://github.com/cometchat/ai-agent-mastra-examples/blob/main/mastra-frontend-actions-agent/widget/index.html) - - Workflows - - [src/mastra/workflows/index.ts](https://github.com/cometchat/ai-agent-mastra-examples/blob/main/mastra-frontend-actions-agent/src/mastra/workflows/index.ts) - *** ## Step 1 - Create the Agent diff --git a/fundamentals/tenor.mdx b/fundamentals/tenor.mdx index bedad15ad..1c334129c 100644 --- a/fundamentals/tenor.mdx +++ b/fundamentals/tenor.mdx @@ -7,7 +7,7 @@ GIFs are a great way to change the tone or convey emotions in your conversations ## Before you begin -1. Sign up at [Tenor](https://tenor.com/developer/dashboard) and create a new app. +1. Sign up at [Tenor](https://tenor.com/) and create a new app. 2. Enter your App name, description and click create. 3. Make note of the API key that has been created. diff --git a/notifications/flutter-push-notifications-ios.mdx b/notifications/flutter-push-notifications-ios.mdx index 6c9f78679..2e020f564 100644 --- a/notifications/flutter-push-notifications-ios.mdx +++ b/notifications/flutter-push-notifications-ios.mdx @@ -78,7 +78,7 @@ Keep the provider IDs—you’ll set them in `CometChatConfig`. ## 3. Local configuration file -Update [`lib/cometchat_config.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/cometchat_config.dart) (or your own config file) so it exposes: +Update [`lib/app_credentials.dart`](https://github.com/cometchat/cometchat-uikit-flutter/blob/v5/sample_app_push_notifications/lib/app_credentials.dart) (or your own config file) so it exposes: ```dart lines class CometChatConfig { From cdf58acb082260a04f9ca3c950572fb62c4e9ece Mon Sep 17 00:00:00 2001 From: omkar6750 Date: Thu, 7 May 2026 21:21:01 +0530 Subject: [PATCH 05/61] fix/changed all community links form http to https --- chat-builder/flutter/overview.mdx | 2 +- chat-builder/react-native/overview.mdx | 2 +- ui-kit/flutter/v6/overview.mdx | 2 +- ui-kit/ios/guide-overview.mdx | 2 +- widget/html/overview.mdx | 2 +- widget/shopify/overview.mdx | 2 +- widget/squarespace/overview.mdx | 2 +- widget/webflow/overview.mdx | 2 +- widget/wix/overview.mdx | 2 +- widget/wordpress/overview.mdx | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/chat-builder/flutter/overview.mdx b/chat-builder/flutter/overview.mdx index 0cc1899c0..fbcfa0013 100644 --- a/chat-builder/flutter/overview.mdx +++ b/chat-builder/flutter/overview.mdx @@ -137,5 +137,5 @@ Experience the CometChat UI Kit Builder in action: ## Need Help? -- Developer Community: http://community.cometchat.com/ +- Developer Community: https://community.cometchat.com/ - Support Portal: https://help.cometchat.com/hc/en-us/requests/new diff --git a/chat-builder/react-native/overview.mdx b/chat-builder/react-native/overview.mdx index 7c4ed01aa..68b527da8 100644 --- a/chat-builder/react-native/overview.mdx +++ b/chat-builder/react-native/overview.mdx @@ -137,6 +137,6 @@ Drop the configuration into your React Native project, add your CometChat creden ## Need Help? -- Developer Community: http://community.cometchat.com/ +- Developer Community: https://community.cometchat.com/ - Dashboard & Live Support: https://app.cometchat.com - UI Kit questions: https://www.cometchat.com/docs/ui-kit/react-native/5.0/getting-started diff --git a/ui-kit/flutter/v6/overview.mdx b/ui-kit/flutter/v6/overview.mdx index 00ec1d9ad..099301264 100644 --- a/ui-kit/flutter/v6/overview.mdx +++ b/ui-kit/flutter/v6/overview.mdx @@ -97,5 +97,5 @@ Upgrade from V5 to V6 with our step-by-step migration guide. If you need assistance, check out: -* [Developer Community](http://community.cometchat.com/) +* [Developer Community](https://community.cometchat.com/) * [Support Portal](https://help.cometchat.com/hc/en-us/requests/new) diff --git a/ui-kit/ios/guide-overview.mdx b/ui-kit/ios/guide-overview.mdx index 3b8cc90aa..cd671acbe 100644 --- a/ui-kit/ios/guide-overview.mdx +++ b/ui-kit/ios/guide-overview.mdx @@ -37,7 +37,7 @@ Use these guides after completing [Getting Started](/ui-kit/ios/getting-started) - [Components Overview](/ui-kit/ios/components-overview) - All available UI components - [Core Features](/ui-kit/ios/core-features) - Messaging, reactions, threads, and more -Need another guide? Request one via the [Developer Community](http://community.cometchat.com/) or Support. +Need another guide? Request one via the [Developer Community](https://community.cometchat.com/) or Support. --- diff --git a/widget/html/overview.mdx b/widget/html/overview.mdx index ebe61b3bd..adf2e28e4 100644 --- a/widget/html/overview.mdx +++ b/widget/html/overview.mdx @@ -115,7 +115,7 @@ Experience the CometChat Widget Builder in action: Connect with other developers and get answers diff --git a/widget/shopify/overview.mdx b/widget/shopify/overview.mdx index 7808d09ec..da78f2793 100644 --- a/widget/shopify/overview.mdx +++ b/widget/shopify/overview.mdx @@ -108,7 +108,7 @@ Experience the CometChat Widget Builder in action: Connect with other developers and get answers diff --git a/widget/squarespace/overview.mdx b/widget/squarespace/overview.mdx index dbd974616..509589376 100644 --- a/widget/squarespace/overview.mdx +++ b/widget/squarespace/overview.mdx @@ -108,7 +108,7 @@ Experience the CometChat Widget Builder in action: Connect with other developers and get answers diff --git a/widget/webflow/overview.mdx b/widget/webflow/overview.mdx index 5ce114640..93a813ff6 100644 --- a/widget/webflow/overview.mdx +++ b/widget/webflow/overview.mdx @@ -108,7 +108,7 @@ Experience the CometChat Widget Builder in action: Connect with other developers and get answers diff --git a/widget/wix/overview.mdx b/widget/wix/overview.mdx index d43d9647e..be2a9ee5b 100644 --- a/widget/wix/overview.mdx +++ b/widget/wix/overview.mdx @@ -108,7 +108,7 @@ Experience the CometChat Widget Builder in action: Connect with other developers and get answers diff --git a/widget/wordpress/overview.mdx b/widget/wordpress/overview.mdx index 5df1ce10a..e30da78af 100644 --- a/widget/wordpress/overview.mdx +++ b/widget/wordpress/overview.mdx @@ -111,7 +111,7 @@ Experience the CometChat Widget Builder in action: Connect with other developers and get answers From 50c08175e6ddbd33b1e18e1e57cd1e6bfd2b67bf Mon Sep 17 00:00:00 2001 From: vivekCometChat Date: Thu, 7 May 2026 23:57:19 +0530 Subject: [PATCH 06/61] stabldocs --- docs.json | 234 +++++++++--------- ui-kit/android/v6/call-features.mdx | 8 +- ui-kit/android/v6/calling-integration.mdx | 8 +- ui-kit/android/v6/getting-started-jetpack.mdx | 4 +- ui-kit/android/v6/getting-started-kotlin.mdx | 4 +- ui-kit/android/v6/getting-started.mdx | 12 +- ui-kit/android/v6/overview.mdx | 4 - 7 files changed, 135 insertions(+), 139 deletions(-) diff --git a/docs.json b/docs.json index 3e9836fb7..475656f3c 100644 --- a/docs.json +++ b/docs.json @@ -1560,123 +1560,6 @@ "dropdown": "Android", "icon": "/images/icons/android.svg", "versions": [ - { - "version": "v5‎‎‎", - "groups": [ - { - "group": " ", - "pages": [ - "ui-kit/android/overview", - { - "group": "Getting Started", - "pages": [ - "ui-kit/android/getting-started", - "ui-kit/android/android-conversation", - "ui-kit/android/android-one-to-one-chat", - "ui-kit/android/android-tab-based-chat", - "ui-kit/android/calling-integration" - ] - }, - { - "group": "Features", - "pages": [ - { - "group": "Chat", - "pages": [ - "ui-kit/android/core-features", - "ui-kit/android/extensions", - "ui-kit/android/ai-features" - ] - }, - "ui-kit/android/call-features" - ] - }, - { - "group": "Theming", - "pages": [ - "ui-kit/android/theme-introduction", - "ui-kit/android/color-resources", - "ui-kit/android/component-styling", - "ui-kit/android/message-bubble-styling", - "ui-kit/android/localize", - "ui-kit/android/sound-manager" - ] - }, - { - "group": "Customization", - "pages": [ - "ui-kit/android/customization-overview", - "ui-kit/android/customization-view-slots", - "ui-kit/android/customization-styles", - "ui-kit/android/customization-viewmodel-data", - "ui-kit/android/customization-adapters", - "ui-kit/android/customization-events", - "ui-kit/android/customization-state-views", - "ui-kit/android/customization-text-formatters", - "ui-kit/android/customization-menu-options", - "ui-kit/android/customization-datasource" - ] - }, - { - "group": "Components", - "pages": [ - "ui-kit/android/components-overview", - "ui-kit/android/conversations", - "ui-kit/android/users", - "ui-kit/android/groups", - "ui-kit/android/group-members", - "ui-kit/android/message-header", - "ui-kit/android/message-list", - "ui-kit/android/message-composer", - "ui-kit/android/compact-message-composer", - "ui-kit/android/message-template", - "ui-kit/android/threaded-messages-header", - "ui-kit/android/incoming-call", - "ui-kit/android/outgoing-call", - "ui-kit/android/call-buttons", - "ui-kit/android/call-logs", - "ui-kit/android/ai-assistant-chat-history", - "ui-kit/android/search" - ] - }, - { - "group": "Reference", - "pages": [ - "ui-kit/android/methods", - "ui-kit/android/events" - ] - }, - { - "group": "Guides", - "pages": [ - "ui-kit/android/guide-overview", - "ui-kit/android/guide-threaded-messages", - "ui-kit/android/guide-block-unblock-user", - "ui-kit/android/guide-new-chat", - "ui-kit/android/guide-message-privately", - "ui-kit/android/guide-search-messages", - "ui-kit/android/guide-call-log-details", - "ui-kit/android/guide-group-chat", - "ui-kit/android/guide-ai-agent", - "ui-kit/android/custom-text-formatter-guide", - "ui-kit/android/mentions-formatter-guide", - "ui-kit/android/shortcut-formatter-guide" - ] - }, - { - "group": "Migration Guide", - "pages": [ - "ui-kit/android/upgrading-from-v4" - ] - }, - "ui-kit/android/troubleshooting", - "ui-kit/android/link/sample", - "ui-kit/android/link/figma", - "ui-kit/android/link/changelog" - ] - } - ] - }, { "version": "v6‎‎‎", "groups": [ @@ -1794,6 +1677,123 @@ } ] }, + { + "version": "v5‎‎‎", + "groups": [ + { + "group": " ", + "pages": [ + "ui-kit/android/overview", + { + "group": "Getting Started", + "pages": [ + "ui-kit/android/getting-started", + "ui-kit/android/android-conversation", + "ui-kit/android/android-one-to-one-chat", + "ui-kit/android/android-tab-based-chat", + "ui-kit/android/calling-integration" + ] + }, + { + "group": "Features", + "pages": [ + { + "group": "Chat", + "pages": [ + "ui-kit/android/core-features", + "ui-kit/android/extensions", + "ui-kit/android/ai-features" + ] + }, + "ui-kit/android/call-features" + ] + }, + { + "group": "Theming", + "pages": [ + "ui-kit/android/theme-introduction", + "ui-kit/android/color-resources", + "ui-kit/android/component-styling", + "ui-kit/android/message-bubble-styling", + "ui-kit/android/localize", + "ui-kit/android/sound-manager" + ] + }, + { + "group": "Customization", + "pages": [ + "ui-kit/android/customization-overview", + "ui-kit/android/customization-view-slots", + "ui-kit/android/customization-styles", + "ui-kit/android/customization-viewmodel-data", + "ui-kit/android/customization-adapters", + "ui-kit/android/customization-events", + "ui-kit/android/customization-state-views", + "ui-kit/android/customization-text-formatters", + "ui-kit/android/customization-menu-options", + "ui-kit/android/customization-datasource" + ] + }, + { + "group": "Components", + "pages": [ + "ui-kit/android/components-overview", + "ui-kit/android/conversations", + "ui-kit/android/users", + "ui-kit/android/groups", + "ui-kit/android/group-members", + "ui-kit/android/message-header", + "ui-kit/android/message-list", + "ui-kit/android/message-composer", + "ui-kit/android/compact-message-composer", + "ui-kit/android/message-template", + "ui-kit/android/threaded-messages-header", + "ui-kit/android/incoming-call", + "ui-kit/android/outgoing-call", + "ui-kit/android/call-buttons", + "ui-kit/android/call-logs", + "ui-kit/android/ai-assistant-chat-history", + "ui-kit/android/search" + ] + }, + { + "group": "Reference", + "pages": [ + "ui-kit/android/methods", + "ui-kit/android/events" + ] + }, + { + "group": "Guides", + "pages": [ + "ui-kit/android/guide-overview", + "ui-kit/android/guide-threaded-messages", + "ui-kit/android/guide-block-unblock-user", + "ui-kit/android/guide-new-chat", + "ui-kit/android/guide-message-privately", + "ui-kit/android/guide-search-messages", + "ui-kit/android/guide-call-log-details", + "ui-kit/android/guide-group-chat", + "ui-kit/android/guide-ai-agent", + "ui-kit/android/custom-text-formatter-guide", + "ui-kit/android/mentions-formatter-guide", + "ui-kit/android/shortcut-formatter-guide" + ] + }, + { + "group": "Migration Guide", + "pages": [ + "ui-kit/android/upgrading-from-v4" + ] + }, + "ui-kit/android/troubleshooting", + "ui-kit/android/link/sample", + "ui-kit/android/link/figma", + "ui-kit/android/link/changelog" + ] + } + ] + }, { "version": "v4‎‎‎", "groups": [ diff --git a/ui-kit/android/v6/call-features.mdx b/ui-kit/android/v6/call-features.mdx index 55fa8540e..ad738b447 100644 --- a/ui-kit/android/v6/call-features.mdx +++ b/ui-kit/android/v6/call-features.mdx @@ -33,8 +33,8 @@ Add the following dependency to your `build.gradle.kts` file: ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta2") - implementation("com.cometchat:calls-sdk-android:4.3.3") + implementation("com.cometchat:chatuikit-kotlin-android:6.0.0") + implementation("com.cometchat:calls-sdk-android:5.0.0-beta.2") } ``` @@ -42,8 +42,8 @@ dependencies { ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta2") - implementation("com.cometchat:calls-sdk-android:4.3.3") + implementation("com.cometchat:chatuikit-compose-android:6.0.0") + implementation("com.cometchat:calls-sdk-android:5.0.0-beta.2") } ``` diff --git a/ui-kit/android/v6/calling-integration.mdx b/ui-kit/android/v6/calling-integration.mdx index 73e6b10d9..825f1692a 100644 --- a/ui-kit/android/v6/calling-integration.mdx +++ b/ui-kit/android/v6/calling-integration.mdx @@ -19,8 +19,8 @@ Add the CometChat Calls SDK dependency alongside your chosen UI Kit module: ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta2") - implementation("com.cometchat:calls-sdk-android:4.3.3") + implementation("com.cometchat:chatuikit-kotlin-android:6.0.0") + implementation("com.cometchat:calls-sdk-android:5.0.0-beta.2") } ``` @@ -28,8 +28,8 @@ dependencies { ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta2") - implementation("com.cometchat:calls-sdk-android:4.3.3") + implementation("com.cometchat:chatuikit-compose-android:6.0.0") + implementation("com.cometchat:calls-sdk-android:5.0.0-beta.2") } ``` diff --git a/ui-kit/android/v6/getting-started-jetpack.mdx b/ui-kit/android/v6/getting-started-jetpack.mdx index 70daf0f25..d43964e57 100644 --- a/ui-kit/android/v6/getting-started-jetpack.mdx +++ b/ui-kit/android/v6/getting-started-jetpack.mdx @@ -98,10 +98,10 @@ android { dependencies { // CometChat Jetpack Compose UI Kit - implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta2") + implementation("com.cometchat:chatuikit-compose-android:6.0.0") // (Optional) Voice/video calling - implementation("com.cometchat:calls-sdk-android:latest") + implementation("com.cometchat:calls-sdk-android:5.0.0-beta.2") } ``` diff --git a/ui-kit/android/v6/getting-started-kotlin.mdx b/ui-kit/android/v6/getting-started-kotlin.mdx index f11460459..7a4988449 100644 --- a/ui-kit/android/v6/getting-started-kotlin.mdx +++ b/ui-kit/android/v6/getting-started-kotlin.mdx @@ -93,10 +93,10 @@ android { dependencies { // CometChat Kotlin UI Kit - implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta2") + implementation("com.cometchat:chatuikit-kotlin-android:6.0.0") // (Optional) Voice/video calling - implementation("com.cometchat:calls-sdk-android:latest") + implementation("com.cometchat:calls-sdk-android:5.0.0-beta.2") } ``` diff --git a/ui-kit/android/v6/getting-started.mdx b/ui-kit/android/v6/getting-started.mdx index af5b47393..3ee397a3f 100644 --- a/ui-kit/android/v6/getting-started.mdx +++ b/ui-kit/android/v6/getting-started.mdx @@ -88,8 +88,8 @@ Inside `libs.versions.toml`, add the versions: ```toml libs.versions.toml [versions] -cometchat-ui-kit = "6.0.0-beta2" -cometchat-calls-sdk = "4.3.3" +cometchat-ui-kit = "6.0.0" +cometchat-calls-sdk = "5.0.0-beta.2" ``` Define the libraries — pick the module for your UI toolkit: @@ -132,10 +132,10 @@ Open the app-level `build.gradle.kts` file and add the dependency for your chose ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-kotlin-android:6.0.0-beta2") + implementation("com.cometchat:chatuikit-kotlin-android:6.0.0") // (Optional) Include if using voice/video calling features - implementation("com.cometchat:calls-sdk-android:4.3.3") + implementation("com.cometchat:calls-sdk-android:5.0.0-beta.2") } ``` @@ -143,10 +143,10 @@ dependencies { ```kotlin build.gradle.kts dependencies { - implementation("com.cometchat:chatuikit-compose-android:6.0.0-beta2") + implementation("com.cometchat:chatuikit-compose-android:6.0.0") // (Optional) Include if using voice/video calling features - implementation("com.cometchat:calls-sdk-android:4.3.3") + implementation("com.cometchat:calls-sdk-android:5.0.0-beta.2") } ``` diff --git a/ui-kit/android/v6/overview.mdx b/ui-kit/android/v6/overview.mdx index c00836192..e3ef30f66 100644 --- a/ui-kit/android/v6/overview.mdx +++ b/ui-kit/android/v6/overview.mdx @@ -4,10 +4,6 @@ sidebarTitle: "Overview" description: "Prebuilt Android Views for chat & messaging, voice & video calling. Supports Kotlin with XML Views and Jetpack Compose." --- - -This is a beta release. APIs and components may change before the stable release. Report issues on [GitHub](https://github.com/cometchat/cometchat-uikit-android/issues). - - | Field | Value | From 94d01eedc3692990515e8e8c58d7569d4460a41b Mon Sep 17 00:00:00 2001 From: rajdubey Date: Fri, 8 May 2026 16:01:21 +0530 Subject: [PATCH 07/61] Added ai assistant and conversation search docs --- .gitignore | 1 + docs.json | 543 +++--- ui-kit/angular/overview.mdx | 2 +- .../search-conversations-service.mdx | 142 ++ .../api-reference/search-messages-service.mdx | 131 ++ ui-kit/angular/v5/call-features.mdx | 2 +- .../cometchat-ai-assistant-chat.mdx | 275 +++ .../cometchat-ai-assistant-message-bubble.mdx | 49 + .../v5/components/cometchat-search.mdx | 830 +++++++++ .../v5/components/cometchat-thread-view.mdx | 162 ++ .../v5/customization/migration-guide.mdx | 1499 +++++++++++++++++ .../v5/guides/custom-message-types.mdx | 370 ++++ .../v5/guides/rich-text-formatting.mdx | 642 +++++++ ui-kit/angular/v5/guides/search-messages.mdx | 208 +++ .../v5/guides/subscription-patterns.mdx | 254 +++ ui-kit/angular/v5/guides/url-formatter.mdx | 150 ++ ui-kit/angular/v5/installation.mdx | 58 + ui-kit/angular/v5/integration.mdx | 6 +- ui-kit/angular/v5/overview.mdx | 13 +- ui-kit/angular/v5/quickstart.mdx | 2 +- ui-kit/angular/v5/troubleshooting.mdx | 2 +- 21 files changed, 5085 insertions(+), 256 deletions(-) create mode 100644 ui-kit/angular/v5/api-reference/search-conversations-service.mdx create mode 100644 ui-kit/angular/v5/api-reference/search-messages-service.mdx create mode 100644 ui-kit/angular/v5/components/cometchat-ai-assistant-chat.mdx create mode 100644 ui-kit/angular/v5/components/cometchat-ai-assistant-message-bubble.mdx create mode 100644 ui-kit/angular/v5/components/cometchat-search.mdx create mode 100644 ui-kit/angular/v5/components/cometchat-thread-view.mdx create mode 100644 ui-kit/angular/v5/customization/migration-guide.mdx create mode 100644 ui-kit/angular/v5/guides/custom-message-types.mdx create mode 100644 ui-kit/angular/v5/guides/rich-text-formatting.mdx create mode 100644 ui-kit/angular/v5/guides/search-messages.mdx create mode 100644 ui-kit/angular/v5/guides/subscription-patterns.mdx create mode 100644 ui-kit/angular/v5/guides/url-formatter.mdx create mode 100644 ui-kit/angular/v5/installation.mdx diff --git a/.gitignore b/.gitignore index 681ba2d12..a5cddc674 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ __pycache__/ /docs-comparison-tool /node_modules /legacy-docs +/ui-kit/angular-dev-v5-docs \ No newline at end of file diff --git a/docs.json b/docs.json index a014a3746..2ec150550 100644 --- a/docs.json +++ b/docs.json @@ -916,7 +916,7 @@ "icon": "/images/icons/react.svg", "versions": [ { - "version": "v5\u200e", + "version": "v5‎", "groups": [ { "group": " ", @@ -1027,7 +1027,7 @@ ] }, { - "version": "v4\u200e", + "version": "v4‎", "groups": [ { "group": " ", @@ -1190,7 +1190,7 @@ ] }, { - "version": "v3\u200e", + "version": "v3‎", "groups": [ { "group": " ", @@ -1212,7 +1212,7 @@ ] }, { - "version": "v2\u200e", + "version": "v2‎", "groups": [ { "group": " ", @@ -1240,7 +1240,7 @@ "icon": "/images/icons/swift.svg", "versions": [ { - "version": "v5\u200e\u200e", + "version": "v5‎‎", "groups": [ { "group": " ", @@ -1347,7 +1347,7 @@ ] }, { - "version": "v4\u200e\u200e", + "version": "v4‎‎", "groups": [ { "group": " ", @@ -1511,7 +1511,7 @@ ] }, { - "version": "v3\u200e\u200e", + "version": "v3‎‎", "groups": [ { "group": " ", @@ -1533,7 +1533,7 @@ ] }, { - "version": "v2\u200e\u200e", + "version": "v2‎‎", "groups": [ { "group": " ", @@ -1561,7 +1561,7 @@ "icon": "/images/icons/android.svg", "versions": [ { - "version": "v5\u200e\u200e\u200e", + "version": "v5‎‎‎", "groups": [ { "group": " ", @@ -1678,7 +1678,7 @@ ] }, { - "version": "v6\u200e\u200e\u200e", + "version": "v6‎‎‎", "groups": [ { "group": " ", @@ -1791,7 +1791,7 @@ ] }, { - "version": "v4\u200e\u200e\u200e", + "version": "v4‎‎‎", "groups": [ { "group": " ", @@ -1948,7 +1948,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e", + "version": "v3‎‎‎", "groups": [ { "group": " ", @@ -1973,7 +1973,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e", + "version": "v2‎‎‎", "groups": [ { "group": " ", @@ -2002,7 +2002,7 @@ "icon": "/images/icons/flutter.svg", "versions": [ { - "version": "v5\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎", "groups": [ { "group": " ", @@ -2106,7 +2106,7 @@ ] }, { - "version": "v6\u200e\u200e\u200e\u200e", + "version": "v6‎‎‎‎", "groups": [ { "group": " ", @@ -2220,7 +2220,7 @@ ] }, { - "version": "v4\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎", "groups": [ { "group": " ", @@ -2396,8 +2396,257 @@ "icon": "/images/icons/angular.svg", "versions": [ { - "version": "v4", + "version": "v5", "default": true, + "groups": [ + { + "group": " ", + "pages": [ + "ui-kit/angular/v5/overview", + { + "group": "Getting Started", + "pages": [ + "ui-kit/angular/v5/installation", + "ui-kit/angular/v5/quickstart", + "ui-kit/angular/v5/integration", + "ui-kit/angular/v5/angular-conversation", + "ui-kit/angular/v5/angular-one-to-one-chat", + "ui-kit/angular/v5/angular-tab-based-chat" + ] + }, + { + "group": "Features", + "pages": [ + { + "group": "Chat", + "pages": [ + "ui-kit/angular/v5/core-features", + "ui-kit/angular/v5/extensions", + "ui-kit/angular/v5/ai-features" + ] + }, + "ui-kit/angular/v5/call-features" + ] + }, + { + "group": "Theming", + "pages": [ + "ui-kit/angular/v5/customization/theming", + "ui-kit/angular/v5/customization/localization", + "ui-kit/angular/v5/customization/global-config", + "ui-kit/angular/v5/customization/date-time-formatting", + "ui-kit/angular/v5/sound-manager" + ] + }, + { + "group": "Components", + "pages": [ + "ui-kit/angular/v5/components/overview", + { + "group": "Conversations", + "pages": [ + "ui-kit/angular/v5/components/cometchat-conversations", + "ui-kit/angular/v5/components/cometchat-conversation-item" + ] + }, + { + "group": "Users", + "pages": [ + "ui-kit/angular/v5/components/cometchat-users", + "ui-kit/angular/v5/components/cometchat-user-item" + ] + }, + { + "group": "Groups", + "pages": [ + "ui-kit/angular/v5/components/cometchat-groups", + "ui-kit/angular/v5/components/cometchat-group-item" + ] + }, + { + "group": "Group Members", + "pages": [ + "ui-kit/angular/v5/components/cometchat-group-members", + "ui-kit/angular/v5/components/cometchat-group-member-item" + ] + }, + { + "group": "Message Header", + "pages": [ + "ui-kit/angular/v5/components/cometchat-message-header" + ] + }, + { + "group": "Search", + "pages": [ + "ui-kit/angular/v5/components/cometchat-search" + ] + }, + { + "group": "Message List", + "pages": [ + "ui-kit/angular/v5/components/cometchat-message-list", + "ui-kit/angular/v5/components/cometchat-flag-message-dialog", + "ui-kit/angular/v5/components/cometchat-typing-indicator" + ] + }, + { + "group": "Message Composer", + "pages": [ + "ui-kit/angular/v5/components/cometchat-message-composer" + ] + }, + { + "group": "Message Bubble", + "pages": [ + "ui-kit/angular/v5/components/cometchat-message-bubble", + "ui-kit/angular/v5/components/cometchat-paginated-list" + ] + }, + { + "group": "Message Information", + "pages": [ + "ui-kit/angular/v5/components/cometchat-message-information" + ] + }, + { + "group": "Thread Header", + "pages": [ + "ui-kit/angular/v5/components/cometchat-thread-header", + "ui-kit/angular/v5/components/cometchat-thread-view" + ] + }, + { + "group": "Message Bubbles", + "pages": [ + "ui-kit/angular/v5/components/cometchat-text-bubble", + "ui-kit/angular/v5/components/cometchat-image-bubble", + "ui-kit/angular/v5/components/cometchat-video-bubble", + "ui-kit/angular/v5/components/cometchat-audio-bubble", + "ui-kit/angular/v5/components/cometchat-file-bubble", + "ui-kit/angular/v5/components/cometchat-action-bubble", + "ui-kit/angular/v5/components/cometchat-delete-bubble", + "ui-kit/angular/v5/components/cometchat-call-bubble", + "ui-kit/angular/v5/components/cometchat-poll-bubble", + "ui-kit/angular/v5/components/cometchat-create-poll", + "ui-kit/angular/v5/components/cometchat-collaborative-document-bubble", + "ui-kit/angular/v5/components/cometchat-collaborative-whiteboard-bubble", + "ui-kit/angular/v5/components/cometchat-sticker-bubble", + "ui-kit/angular/v5/components/cometchat-stickers-keyboard" + ] + }, + { + "group": "Reactions", + "pages": [ + "ui-kit/angular/v5/components/cometchat-reactions", + "ui-kit/angular/v5/components/cometchat-reaction-list", + "ui-kit/angular/v5/components/cometchat-reaction-info" + ] + }, + { + "group": "Calls", + "pages": [ + "ui-kit/angular/v5/components/cometchat-call-buttons", + "ui-kit/angular/v5/components/cometchat-incoming-call", + "ui-kit/angular/v5/components/cometchat-outgoing-call", + "ui-kit/angular/v5/components/cometchat-ongoing-call", + "ui-kit/angular/v5/components/cometchat-call-logs" + ] + }, + { + "group": "AI", + "pages": [ + "ui-kit/angular/v5/components/cometchat-smart-replies", + "ui-kit/angular/v5/components/cometchat-conversation-starter", + "ui-kit/angular/v5/components/cometchat-conversation-summary", + "ui-kit/angular/v5/components/cometchat-ai-assistant-chat", + "ui-kit/angular/v5/components/cometchat-ai-assistant-chat-history", + "ui-kit/angular/v5/components/cometchat-ai-assistant-message-bubble", + "ui-kit/angular/v5/components/cometchat-stream-message-bubble", + "ui-kit/angular/v5/components/cometchat-toolcall-argument-bubble", + "ui-kit/angular/v5/components/cometchat-toolcall-result-bubble", + "ui-kit/angular/v5/components/cometchat-markdown-renderer" + ] + }, + { + "group": "Base Elements", + "pages": [ + "ui-kit/angular/v5/components/avatar", + "ui-kit/angular/v5/components/button", + "ui-kit/angular/v5/components/list-item", + "ui-kit/angular/v5/components/date", + "ui-kit/angular/v5/components/message-preview", + "ui-kit/angular/v5/components/cometchat-search-bar", + "ui-kit/angular/v5/components/cometchat-checkbox", + "ui-kit/angular/v5/components/cometchat-radio-button", + "ui-kit/angular/v5/components/cometchat-dropdown", + "ui-kit/angular/v5/components/cometchat-toast", + "ui-kit/angular/v5/components/action-sheet", + "ui-kit/angular/v5/components/emoji-keyboard", + "ui-kit/angular/v5/components/cometchat-popover", + "ui-kit/angular/v5/components/context-menu", + "ui-kit/angular/v5/components/cometchat-change-scope", + "ui-kit/angular/v5/components/cometchat-media-recorder", + "ui-kit/angular/v5/components/confirm-dialog", + "ui-kit/angular/v5/components/cometchat-fullscreen-viewer", + "ui-kit/angular/v5/components/cometchat-error-boundary", + "ui-kit/angular/v5/components/cometchat-link-dialog", + "ui-kit/angular/v5/components/cometchat-link-popover" + ] + } + ] + }, + { + "group": "Reference", + "pages": [ + "ui-kit/angular/v5/events", + "ui-kit/angular/v5/methods", + "ui-kit/angular/v5/accessibility", + "ui-kit/angular/v5/troubleshooting" + ] + }, + { + "group": "Guides", + "pages": [ + "ui-kit/angular/v5/guides/overview", + "ui-kit/angular/v5/guides/state-management", + "ui-kit/angular/v5/guides/threaded-messages", + "ui-kit/angular/v5/guides/group-chat", + "ui-kit/angular/v5/guides/new-chat", + "ui-kit/angular/v5/guides/block-unblock-user", + "ui-kit/angular/v5/guides/message-privately", + "ui-kit/angular/v5/guides/call-log-details", + "ui-kit/angular/v5/guides/search-messages", + "ui-kit/angular/v5/guides/rich-text-formatting", + "ui-kit/angular/v5/guides/custom-message-types", + "ui-kit/angular/v5/guides/custom-text-formatter", + "ui-kit/angular/v5/guides/mentions-formatter", + "ui-kit/angular/v5/guides/shortcut-formatter", + "ui-kit/angular/v5/guides/hashtag-formatter", + "ui-kit/angular/v5/guides/url-formatter", + "ui-kit/angular/v5/guides/subscription-patterns" + ] + }, + { + "group": "Services", + "pages": [ + "ui-kit/angular/v5/api-reference/introduction", + "ui-kit/angular/v5/api-reference/chat-state-service", + "ui-kit/angular/v5/api-reference/message-bubble-config-service", + "ui-kit/angular/v5/api-reference/templates-service", + "ui-kit/angular/v5/api-reference/search-conversations-service", + "ui-kit/angular/v5/api-reference/search-messages-service", + "ui-kit/angular/v5/api-reference/formatter-config-service", + "ui-kit/angular/v5/api-reference/rich-text-editor-service" + ] + }, + "ui-kit/angular/v5/customization/migration-guide" + ] + } + ] + }, + { + "version": "v4", "groups": [ { "group": " ", @@ -2587,201 +2836,7 @@ ] }, { - "version": "v5", - "groups": [ - { - "group": " ", - "pages": [ - "ui-kit/angular/v5/overview", - { - "group": "Getting Started", - "pages": [ - "ui-kit/angular/v5/quickstart", - "ui-kit/angular/v5/integration", - "ui-kit/angular/v5/angular-conversation", - "ui-kit/angular/v5/angular-one-to-one-chat", - "ui-kit/angular/v5/angular-tab-based-chat" - ] - }, - { - "group": "Features", - "pages": [ - { - "group": "Chat", - "pages": [ - "ui-kit/angular/v5/core-features", - "ui-kit/angular/v5/extensions", - "ui-kit/angular/v5/ai-features" - ] - }, - "ui-kit/angular/v5/call-features" - ] - }, - { - "group": "Theming", - "pages": [ - "ui-kit/angular/v5/customization/theming", - "ui-kit/angular/v5/customization/localization", - "ui-kit/angular/v5/customization/global-config", - "ui-kit/angular/v5/sound-manager" - ] - }, - { - "group": "Components", - "pages": [ - "ui-kit/angular/v5/components/overview", - { - "group": "Conversations", - "pages": [ - "ui-kit/angular/v5/components/cometchat-conversations", - "ui-kit/angular/v5/components/cometchat-conversation-item" - ] - }, - { - "group": "Users", - "pages": [ - "ui-kit/angular/v5/components/cometchat-users", - "ui-kit/angular/v5/components/cometchat-user-item" - ] - }, - { - "group": "Groups", - "pages": [ - "ui-kit/angular/v5/components/cometchat-groups", - "ui-kit/angular/v5/components/cometchat-group-item" - ] - }, - { - "group": "Group Members", - "pages": [ - "ui-kit/angular/v5/components/cometchat-group-members", - "ui-kit/angular/v5/components/cometchat-group-member-item" - ] - }, - { - "group": "Message Header", - "pages": [ - "ui-kit/angular/v5/components/cometchat-message-header" - ] - }, - { - "group": "Message List", - "pages": [ - "ui-kit/angular/v5/components/cometchat-message-list" - ] - }, - { - "group": "Message Composer", - "pages": [ - "ui-kit/angular/v5/components/cometchat-message-composer" - ] - }, - { - "group": "Message Bubble", - "pages": [ - "ui-kit/angular/v5/components/cometchat-message-bubble" - ] - }, - { - "group": "Message Information", - "pages": [ - "ui-kit/angular/v5/components/cometchat-message-information" - ] - }, - { - "group": "Thread Header", - "pages": [ - "ui-kit/angular/v5/components/cometchat-thread-header" - ] - }, - { - "group": "Message Bubbles", - "pages": [ - "ui-kit/angular/v5/components/cometchat-text-bubble", - "ui-kit/angular/v5/components/cometchat-image-bubble", - "ui-kit/angular/v5/components/cometchat-video-bubble", - "ui-kit/angular/v5/components/cometchat-audio-bubble", - "ui-kit/angular/v5/components/cometchat-file-bubble", - "ui-kit/angular/v5/components/cometchat-action-bubble", - "ui-kit/angular/v5/components/cometchat-delete-bubble", - "ui-kit/angular/v5/components/cometchat-call-bubble", - "ui-kit/angular/v5/components/cometchat-poll-bubble", - "ui-kit/angular/v5/components/cometchat-sticker-bubble", - "ui-kit/angular/v5/components/cometchat-stickers-keyboard", - "ui-kit/angular/v5/components/cometchat-collaborative-document-bubble", - "ui-kit/angular/v5/components/cometchat-collaborative-whiteboard-bubble" - ] - }, - { - "group": "Reactions", - "pages": [ - "ui-kit/angular/v5/components/cometchat-reactions", - "ui-kit/angular/v5/components/cometchat-reaction-list", - "ui-kit/angular/v5/components/cometchat-reaction-info" - ] - }, - { - "group": "Calls", - "pages": [ - "ui-kit/angular/v5/components/cometchat-call-buttons", - "ui-kit/angular/v5/components/cometchat-incoming-call", - "ui-kit/angular/v5/components/cometchat-outgoing-call", - "ui-kit/angular/v5/components/cometchat-call-logs" - ] - }, - { - "group": "AI", - "pages": [ - "ui-kit/angular/v5/components/cometchat-conversation-starter", - "ui-kit/angular/v5/components/cometchat-smart-replies", - "ui-kit/angular/v5/components/cometchat-conversation-summary" - ] - } - ] - }, - { - "group": "Reference", - "pages": [ - "ui-kit/angular/v5/events", - "ui-kit/angular/v5/methods", - "ui-kit/angular/v5/accessibility", - "ui-kit/angular/v5/troubleshooting" - ] - }, - { - "group": "Guides", - "pages": [ - "ui-kit/angular/v5/guides/overview", - "ui-kit/angular/v5/guides/threaded-messages", - "ui-kit/angular/v5/guides/block-unblock-user", - "ui-kit/angular/v5/guides/new-chat", - "ui-kit/angular/v5/guides/message-privately", - "ui-kit/angular/v5/guides/call-log-details", - "ui-kit/angular/v5/guides/group-chat", - "ui-kit/angular/v5/guides/custom-text-formatter", - "ui-kit/angular/v5/guides/mentions-formatter", - "ui-kit/angular/v5/guides/hashtag-formatter", - "ui-kit/angular/v5/guides/shortcut-formatter", - "ui-kit/angular/v5/guides/state-management" - ] - }, - { - "group": "Services", - "pages": [ - "ui-kit/angular/v5/api-reference/introduction", - "ui-kit/angular/v5/api-reference/chat-state-service", - "ui-kit/angular/v5/api-reference/templates-service", - "ui-kit/angular/v5/api-reference/formatter-config-service", - "ui-kit/angular/v5/api-reference/rich-text-editor-service", - "ui-kit/angular/v5/api-reference/message-bubble-config-service" - ] - } - ] - } - ] - }, - { - "version": "v3\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎", "groups": [ { "group": " ", @@ -2802,7 +2857,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎", "groups": [ { "group": " ", @@ -2829,7 +2884,7 @@ "icon": "/images/icons/vuejs.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -2922,7 +2977,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -2944,7 +2999,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -2976,7 +3031,7 @@ "icon": "/images/icons/js.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3068,7 +3123,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3191,7 +3246,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3294,7 +3349,7 @@ "icon": "/images/icons/react.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3382,7 +3437,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3496,7 +3551,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3598,7 +3653,7 @@ "icon": "/images/icons/swift.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3687,7 +3742,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3805,7 +3860,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -3912,7 +3967,7 @@ "icon": "/images/icons/android.svg", "versions": [ { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4013,7 +4068,7 @@ ] }, { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4107,7 +4162,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4227,7 +4282,7 @@ ] }, { - "version": "v2\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v2‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4330,7 +4385,7 @@ "icon": "/images/icons/flutter.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4422,7 +4477,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -4513,7 +4568,7 @@ ] }, { - "version": "v3\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v3‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5118,7 +5173,7 @@ "icon": "/images/icons/js.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5160,7 +5215,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5244,7 +5299,7 @@ "icon": "/images/icons/react.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5285,7 +5340,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5359,7 +5414,7 @@ "icon": "/images/icons/swift.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5400,7 +5455,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5474,7 +5529,7 @@ "icon": "/images/icons/android.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5514,7 +5569,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -5588,7 +5643,7 @@ "icon": "/images/icons/flutter.svg", "versions": [ { - "version": "v4\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v4‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": " ", @@ -5628,7 +5683,7 @@ ] }, { - "version": "v5\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e\u200e", + "version": "v5‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎", "groups": [ { "group": "Overview", @@ -9655,7 +9710,7 @@ "metatags": { "charset": "UTF-8", "viewport": "width=device-width, initial-scale=1.0", - "description": "Learn how to integrate, customize, and scale real-time chat using CometChat\u2019s UI Kits, SDKs, and widgets across popular frameworks. Get step-by-step guides, best practices, and implementation details to build production-ready chat experiences.", + "description": "Learn how to integrate, customize, and scale real-time chat using CometChat’s UI Kits, SDKs, and widgets across popular frameworks. Get step-by-step guides, best practices, and implementation details to build production-ready chat experiences.", "language": "en" } }, @@ -9664,4 +9719,4 @@ "redirect": true } } -} +} \ No newline at end of file diff --git a/ui-kit/angular/overview.mdx b/ui-kit/angular/overview.mdx index 57ad6b1b5..e31ad5b0f 100644 --- a/ui-kit/angular/overview.mdx +++ b/ui-kit/angular/overview.mdx @@ -4,7 +4,7 @@ description: "Overview of Overview in CometChat." --- -🚀 **CometChat Angular UI Kit v5 is now available in beta!** It features a completely revamped component architecture and improved theming. [Try the v5 Beta →](/ui-kit/angular/v5/overview) +🚀 **CometChat Angular UI Kit v5 is now available!** It features a completely revamped component architecture, standalone components, CSS variable theming, and improved customization. [Switch to v5 →](/ui-kit/angular/v5/overview) diff --git a/ui-kit/angular/v5/api-reference/search-conversations-service.mdx b/ui-kit/angular/v5/api-reference/search-conversations-service.mdx new file mode 100644 index 000000000..540718d81 --- /dev/null +++ b/ui-kit/angular/v5/api-reference/search-conversations-service.mdx @@ -0,0 +1,142 @@ +--- +title: Search Conversations Service +description: Signal-based service for conversation search state, SDK queries, pagination, and real-time listener management. +--- + +The `SearchConversationsService` manages conversation search state using Angular signals. It handles SDK query building, pagination, and attaches real-time listeners for message, user status, group, call, typing, and receipt events to keep results up to date. + +## Import + +```typescript +import { SearchConversationsService } from '@cometchat/chat-uikit-angular'; +``` + +## Signals (Reactive State) + +All state is exposed as Angular `WritableSignal` values. Components read these in templates or computed signals for automatic change detection. + +| Signal | Type | Default | Description | +|--------|------|---------|-------------| +| `conversations` | `WritableSignal` | `[]` | Current conversation search results | +| `fetchState` | `WritableSignal` | `States.loaded` | Current fetch state: `loading`, `loaded`, `empty`, or `error` | +| `hasMoreResults` | `WritableSignal` | `false` | Whether more pages of results are available | +| `typingIndicatorMap` | `WritableSignal>` | `new Map()` | Active typing indicators keyed by user UID or group GUID | + +## Methods + +### search() + +Initiates a conversation search. Resets state, validates criteria, builds the SDK request, and fetches the first page. + +```typescript +async search( + keyword: string, + filters: CometChatSearchFilter[], + customBuilder?: CometChat.ConversationsRequestBuilder +): Promise +``` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `keyword` | `string` | Search keyword. Empty string with no conversation filters results in `States.empty` | +| `filters` | `CometChatSearchFilter[]` | Active filters. `Unread` sets unread-only, `Groups` sets group conversation type | +| `customBuilder` | `ConversationsRequestBuilder` | Optional custom builder that overrides the default | + +The method sets `fetchState` to `loading` immediately, then to `loaded` (with results), `empty` (no results), or `error` (SDK failure). + +### loadMore() + +Fetches the next page of results and appends to the existing list. Deduplicates by conversation ID. + +```typescript +async loadMore(): Promise +``` + +### attachListeners() + +Attaches all real-time SDK listeners (message, user, group, call, typing, receipt). Call this after obtaining the logged-in user. + +```typescript +attachListeners(loggedInUser: CometChat.User): void +``` + +Listeners attached: +- **Message listener**: new text/media/custom messages, edits, deletes +- **User listener**: online/offline status changes +- **Group listener**: member joined/left/kicked/banned/added, scope changes +- **Call listener**: incoming/outgoing/accepted/rejected/cancelled calls +- **Typing listener**: typing started/ended indicators +- **Receipt listener**: delivered/read receipts (via `CometChatMessageEvents`) + +### detachListeners() + +Removes all SDK listeners and unsubscribes from receipt observables. + +```typescript +detachListeners(): void +``` + +### reset() + +Detaches listeners and resets all signals to their default values. + +```typescript +reset(): void +``` + +## Request Building + +The service builds a `CometChat.ConversationsRequest` based on the keyword and active filters: + +- Sets `setSearchKeyword()` when keyword is non-empty +- Sets `setUnread(true)` when the `Unread` filter is active +- Sets `setConversationType('group')` when the `Groups` filter is active +- Limit is 3 when no filters are active (preview mode), 30 when filters are active (full list mode) +- Custom builder overrides the default when provided + +## Real-Time Event Handling + +| Event | Behavior | +|-------|----------| +| New message received | Updates last message, increments unread count (if not from logged-in user), moves conversation to top | +| Message edited/deleted | Updates last message in the matching conversation | +| User online/offline | Updates the `conversationWith` user status | +| Typing started | Adds entry to `typingIndicatorMap` | +| Typing ended | Removes entry from `typingIndicatorMap` | +| Message delivered | Updates `deliveredAt` on the last message | +| Message read | Updates `readAt` on the last message, resets unread count to 0 | +| Group member left/kicked/banned (self) | Removes the conversation from results | +| Group member left/kicked/banned (other) | Treats as a new message (action message) | + +## Usage + +The service is typically used internally by `CometChatSearchConversationsListComponent`, but can be injected directly for custom implementations: + +```typescript expandable +import { Component, inject, effect } from '@angular/core'; +import { SearchConversationsService } from '@cometchat/chat-uikit-angular'; +import { CometChatSearchFilter, States } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-custom-search', + template: ` + @switch (service.fetchState()) { + @case (States.loading) {

Searching...

} + @case (States.empty) {

No conversations found

} + @case (States.loaded) { + @for (conv of service.conversations(); track conv.getConversationId()) { +
{{ conv.getConversationWith().getName() }}
+ } + } + } + ` +}) +export class CustomSearchComponent { + readonly service = inject(SearchConversationsService); + readonly States = States; + + search(keyword: string): void { + this.service.search(keyword, [CometChatSearchFilter.Unread]); + } +} +``` diff --git a/ui-kit/angular/v5/api-reference/search-messages-service.mdx b/ui-kit/angular/v5/api-reference/search-messages-service.mdx new file mode 100644 index 000000000..e87577f10 --- /dev/null +++ b/ui-kit/angular/v5/api-reference/search-messages-service.mdx @@ -0,0 +1,131 @@ +--- +title: Search Messages Service +description: Signal-based service for message search state, SDK queries, attachment-type filtering, and pagination. +--- + +The `SearchMessagesService` manages message search state using Angular signals. It handles SDK query building with keyword, attachment-type, and user/group scoping, plus pagination for loading more results. + +## Import + +```typescript +import { SearchMessagesService } from '@cometchat/chat-uikit-angular'; +``` + +## Signals (Reactive State) + +| Signal | Type | Default | Description | +|--------|------|---------|-------------| +| `messages` | `WritableSignal` | `[]` | Current message search results (newest first after reversal) | +| `fetchState` | `WritableSignal` | `States.loaded` | Current fetch state: `loading`, `loaded`, `empty`, or `error` | +| `hasMoreResults` | `WritableSignal` | `false` | Whether more pages of results are available | + +## Methods + +### search() + +Initiates a message search. Resets state, validates criteria, builds the SDK request, and fetches the first page. + +```typescript +async search( + keyword: string, + filters: CometChatSearchFilter[], + uid?: string, + guid?: string, + customBuilder?: CometChat.MessagesRequestBuilder, + alwaysShowSeeMore?: boolean +): Promise +``` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `keyword` | `string` | Search keyword | +| `filters` | `CometChatSearchFilter[]` | Active filters that determine attachment type filtering | +| `uid` | `string` | Optional user UID to scope search to a specific user's messages | +| `guid` | `string` | Optional group GUID to scope search to a specific group's messages | +| `customBuilder` | `MessagesRequestBuilder` | Optional custom builder that overrides the default | +| `alwaysShowSeeMore` | `boolean` | When true, limits initial results to 3 (preview mode with "See more" button) | + +Validation: search requires at least one of: non-empty keyword, active message filter, uid, or guid. Otherwise `fetchState` is set to `States.empty`. + +### loadMore() + +Fetches the next page of results and appends to the existing list. + +```typescript +async loadMore(): Promise +``` + +### reset() + +Resets all signals to their default values and clears the internal request reference. + +```typescript +reset(): void +``` + +## Request Building + +The service builds a `CometChat.MessagesRequest` based on the search criteria: + +- `hideDeletedMessages(true)` — always excludes deleted messages +- `setSearchKeyword()` — when keyword is non-empty +- `setUID()` — when uid is provided (scoped to user) +- `setGUID()` — when guid is provided (scoped to group) +- `hasLinks(true)` — when the `Links` filter is active +- `setAttachmentTypes()` — maps filters to SDK attachment types: + +| Filter | SDK Attachment Type | +|--------|-------------------| +| `Photos` | `CometChat.AttachmentType.IMAGE` | +| `Videos` | `CometChat.AttachmentType.VIDEO` | +| `Documents` | `CometChat.AttachmentType.FILE` | +| `Audio` | `CometChat.AttachmentType.AUDIO` | + +- Limit is 3 when `alwaysShowSeeMore` is true (preview mode), 30 otherwise +- Results are reversed after fetch to show newest first + +## Usage + +The service is typically used internally by `CometChatSearchMessagesListComponent`, but can be injected directly: + +```typescript expandable +import { Component, inject } from '@angular/core'; +import { SearchMessagesService } from '@cometchat/chat-uikit-angular'; +import { CometChatSearchFilter, States } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-message-search', + template: ` + @if (service.fetchState() === States.loaded) { + @for (msg of service.messages(); track msg.getId()) { +
{{ msg.getSender()?.getName() }}: {{ msg.getType() }}
+ } + @if (service.hasMoreResults()) { + + } + } + ` +}) +export class MessageSearchComponent { + readonly service = inject(SearchMessagesService); + readonly States = States; + + searchPhotos(keyword: string): void { + this.service.search(keyword, [CometChatSearchFilter.Photos]); + } + + searchInGroup(keyword: string, guid: string): void { + this.service.search(keyword, [], undefined, guid); + } +} +``` + +## Differences from SearchConversationsService + +| Aspect | SearchConversationsService | SearchMessagesService | +|--------|--------------------------|---------------------| +| Real-time listeners | Yes (message, user, group, call, typing, receipt) | No | +| Scoping | Keyword + filters only | Keyword + filters + uid/guid | +| Attachment filtering | N/A | Photos, Videos, Documents, Audio, Links | +| Typing indicators | Yes (`typingIndicatorMap` signal) | No | +| Preview mode | 3 results when no filters | 3 results when `alwaysShowSeeMore` is true | diff --git a/ui-kit/angular/v5/call-features.mdx b/ui-kit/angular/v5/call-features.mdx index 99bb9eced..24d135e42 100644 --- a/ui-kit/angular/v5/call-features.mdx +++ b/ui-kit/angular/v5/call-features.mdx @@ -16,7 +16,7 @@ Ensure `CometChatUIKit.init()` has completed and the user is logged in before us Install the Calls SDK: ```bash -npm install @cometchat/calls-sdk-javascript@5.0.0-beta.1 +npm install @cometchat/calls-sdk-javascript ``` Then enable calling in your `UIKitSettingsBuilder`: diff --git a/ui-kit/angular/v5/components/cometchat-ai-assistant-chat.mdx b/ui-kit/angular/v5/components/cometchat-ai-assistant-chat.mdx new file mode 100644 index 000000000..b6d470cd7 --- /dev/null +++ b/ui-kit/angular/v5/components/cometchat-ai-assistant-chat.mdx @@ -0,0 +1,275 @@ +--- +title: "CometChatAIAssistantChat" +description: "Top-level orchestrator component for the AI assistant chat experience with real-time streaming, tool call dispatch, chat history, and suggestion pills." +--- + +## Overview + +`CometChatAIAssistantChat` is the top-level orchestrator for the AI assistant chat experience. It wires together `CometChatMessageHeader`, `CometChatMessageList`, `CometChatMessageComposer`, and the `CometChatAIAssistantChatHistory` sidebar into a single cohesive interface. + +Key capabilities: + +- **Real-time streaming** — integrates with `CometChatAIStreamingService` to display live AI responses token-by-token +- **Tool call dispatch** — registers `CometChatAIAssistantTools` handlers that the streaming service invokes automatically +- **Chat history sidebar** — browse and resume past AI conversations without losing context +- **Suggestion pills** — configurable quick-reply prompts sourced from explicit input or user metadata +- **Concurrent run support** — multiple AI responses can stream simultaneously without state corruption +- **Keyboard accessible** — full focus management including sidebar open/close focus trapping and restoration + +## Basic Usage + +```typescript +import { Component, OnInit } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatAIAssistantChat, + CometChatAIStreamingService, +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-ai-chat', + standalone: true, + imports: [CometChatAIAssistantChat], + template: ` + @if (agentUser) { + + } + `, +}) +export class AiChatComponent implements OnInit { + agentUser: CometChat.User | null = null; + + constructor(private streamingService: CometChatAIStreamingService) {} + + ngOnInit(): void { + CometChat.getUser('ai-agent-uid').then((user) => { + this.agentUser = user; + }); + + // Wire the CometChat SDK WebSocket to the streaming service + CometChat.addMessageListener( + 'ai-listener', + new CometChat.MessageListener({ + onAIAssistantMessageReceived: (event: CometChat.AIAssistantBaseEvent) => { + this.streamingService.handleWebsocketMessage(event, 'ai-agent-uid'); + }, + }) + ); + } +} +``` + +## Inputs + +| Input | Type | Default | Description | +|-------|------|---------|-------------| +| `user` | `CometChat.User` | required | The AI agent user to chat with | +| `streamingSpeed` | `number` | `30` | Per-token delay in milliseconds for the streaming animation | +| `aiAssistantTools` | `CometChatAIAssistantTools` | `undefined` | Tool handlers the AI can invoke during a conversation | +| `loadLastAgentConversation` | `boolean` | `false` | When `true`, automatically resumes the most recent conversation on load | +| `showSuggestedMessages` | `boolean` | `true` | Whether to show suggestion pills above the composer | +| `suggestedMessages` | `string[]` | `[]` | Explicit suggestion pill labels; falls back to `user.getMetadata().suggestedMessages` | +| `greetingTemplate` | `TemplateRef<{ $implicit: CometChat.User }>` | `undefined` | Custom greeting template rendered before the first message | + +## Events + +This component does not emit outputs directly — it manages all state internally and communicates with child components via signals and the `CometChatAIStreamingService`. + +## Advanced Usage + +### Registering Tool Handlers + +Use `CometChatAIAssistantTools` to register client-side functions the AI can call during a conversation. The streaming service dispatches them automatically when a `tool_call_end` event is received. + +```typescript +import { Component, OnInit } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatAIAssistantChat, + CometChatAIAssistantTools, + CometChatAIStreamingService, +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-ai-chat-with-tools', + standalone: true, + imports: [CometChatAIAssistantChat], + template: ` + @if (agentUser) { + + } + `, +}) +export class AiChatWithToolsComponent implements OnInit { + agentUser: CometChat.User | null = null; + + tools = new CometChatAIAssistantTools({ + get_weather: async (args: { city: string }) => { + const response = await fetch(`/api/weather?city=${args.city}`); + return response.json(); + }, + search_products: async (args: { query: string; limit?: number }) => { + const response = await fetch(`/api/products?q=${args.query}&limit=${args.limit ?? 5}`); + return response.json(); + }, + }); + + constructor(private streamingService: CometChatAIStreamingService) {} + + ngOnInit(): void { + CometChat.getUser('ai-agent-uid').then((user) => { + this.agentUser = user; + }); + + CometChat.addMessageListener( + 'ai-listener', + new CometChat.MessageListener({ + onAIAssistantMessageReceived: (event: CometChat.AIAssistantBaseEvent) => { + this.streamingService.handleWebsocketMessage(event, 'ai-agent-uid'); + }, + }) + ); + } +} +``` + +### Suggestion Pills + +Provide quick-reply prompts to guide users. If `suggestedMessages` is empty, the component falls back to `user.getMetadata().suggestedMessages`. + +```typescript +@Component({ + template: ` + @if (agentUser) { + + } + `, +}) +export class AiChatSuggestionsComponent { + agentUser: CometChat.User | null = null; + + suggestions = [ + 'What can you help me with?', + 'Show me recent orders', + 'How do I reset my password?', + ]; +} +``` + +### Custom Greeting Template + +Render a personalized greeting before the first message using the `greetingTemplate` input. The template context exposes the agent user as `$implicit`. + +```typescript +@Component({ + standalone: true, + imports: [CometChatAIAssistantChat], + template: ` + @if (agentUser) { + + } + + +
+

Hi, I'm {{ user.getName() }}

+

Ask me anything about your account or our products.

+
+
+ `, +}) +export class AiChatGreetingComponent { + agentUser: CometChat.User | null = null; +} +``` + +### Concurrent Runs + +The component supports multiple simultaneous AI responses. Each run streams independently — a slow tool call in one run does not block text streaming in another. This is handled automatically by `CometChatAIStreamingService` using per-run `concatMap` pipelines merged via `mergeMap`. + +```typescript +// Wire multiple agent users — each gets its own isolated streaming state +CometChat.addMessageListener( + 'ai-listener', + new CometChat.MessageListener({ + onAIAssistantMessageReceived: (event: CometChat.AIAssistantBaseEvent) => { + // chatId scopes the streaming state — use the agent's UID + const chatId = event.getReceiverId(); + this.streamingService.handleWebsocketMessage(event, chatId); + }, + }) +); +``` + +### Controlling Streaming Speed + +Adjust the per-token animation delay at runtime. The `streamingSpeed` input is synced to the service via an `effect()` — changes take effect immediately for all active and future runs. + +```typescript +@Component({ + template: ` + @if (agentUser) { + + } + + `, +}) +export class AiChatSpeedComponent { + agentUser: CometChat.User | null = null; + speedMs = 30; +} +``` + +## Customization + +### CSS Variables + +All visual properties are controlled via CSS variables. Override them in your global stylesheet or a scoped component style: + +```css +cometchat-ai-assistant-chat { + --cometchat-primary-color: #7c3aed; + --cometchat-background-color-01: #f9fafb; + --cometchat-text-color-primary: #111827; + --cometchat-radius-2: 12px; +} +``` + +### Localization Keys + +| Key | Default (en-us) | +|-----|-----------------| +| `ai_assistant_chat_intro_message` | "How can I help you today?" | +| `ai_assistant_chat_composer_placeholder` | "Ask me anything..." | +| `ai_assistant_chat_new_chat` | "New Chat" | +| `ai_assistant_chat_history_title` | "AI Assistant" | +| `ai_assistant_chat_thinking` | "Thinking..." | +| `ai_assistant_chat_executing_tool` | "Executing..." | +| `ai_assistant_chat_no_internet` | "No internet connection" | + +Override any key via `CometChatLocalize.updateKeys()`: + +```typescript +import { CometChatLocalize } from '@cometchat/chat-uikit-angular'; + +CometChatLocalize.updateKeys('en', { + ai_assistant_chat_intro_message: 'Hello! How can I assist you today?', + ai_assistant_chat_composer_placeholder: 'Type your question...', +}); +``` diff --git a/ui-kit/angular/v5/components/cometchat-ai-assistant-message-bubble.mdx b/ui-kit/angular/v5/components/cometchat-ai-assistant-message-bubble.mdx new file mode 100644 index 000000000..ad773d144 --- /dev/null +++ b/ui-kit/angular/v5/components/cometchat-ai-assistant-message-bubble.mdx @@ -0,0 +1,49 @@ +--- +title: "CometChatAIAssistantMessageBubble" +description: "Renders a completed AI assistant message with rich markdown formatting including code blocks, tables, lists, and links" +--- + +The `CometChatAIAssistantMessageBubble` component renders a completed AI assistant response with full markdown support. It is used inside `CometChatMessageBubble` when the message type is `assistant` (agentic category). + +## Overview + +- **Markdown rendering**: Full markdown via `CometChatMarkdownRenderer` — headings, bold/italic, code blocks with copy button, tables, lists, blockquotes, links +- **Image viewer**: Clicking images in the response opens a fullscreen dialog +- **Dark mode**: Automatically adapts to the system color scheme +- **Performance**: `content-visibility: auto` for efficient rendering in long lists + +## Basic Usage + +```typescript +import { CometChatAIAssistantMessageBubble } from '@cometchat/chat-uikit-angular'; + +@Component({ + standalone: true, + imports: [CometChatAIAssistantMessageBubble], + template: ` + + + ` +}) +export class MyComponent { + aiMessage!: CometChat.AIAssistantMessage; +} +``` + +## Properties + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `message` | `CometChat.AIAssistantMessage` | ✅ Yes | The completed AI assistant message to render | + +## Events + +This component has no output events. Image clicks are handled internally via `CometChatUIEvents.ccShowDialog`. + +## Related Components + +- **CometChatStreamMessageBubble** — for live-streaming AI responses +- **CometChatMarkdownRenderer** — the underlying markdown renderer +- **CometChatMessageBubble** — the parent bubble container +- **CometChatAIAssistantChat** — the full AI chat interface diff --git a/ui-kit/angular/v5/components/cometchat-search.mdx b/ui-kit/angular/v5/components/cometchat-search.mdx new file mode 100644 index 000000000..71fe9e787 --- /dev/null +++ b/ui-kit/angular/v5/components/cometchat-search.mdx @@ -0,0 +1,830 @@ +--- +title: "CometChatSearch" +description: "A unified Angular search component for cross-entity search across conversations and messages with filter chips, scoped search, and template customization" +--- + +## Overview + +The CometChatSearch component provides a unified search experience across conversations and messages. It combines a debounced search input, a filter chip bar, and two lazy-loaded result sections (conversations and messages) into a single, cohesive UI. + +The component follows a **service-driven architecture**: +- **SearchConversationsService** handles conversation search queries, pagination, and real-time listener updates +- **SearchMessagesService** handles message search queries, pagination, and attachment-type filtering +- **CometChatTemplatesService** provides template resolution with a priority chain: `@Input` → service template → shared template → default +- **Component @Input properties** allow per-instance overrides for scoping, filtering, and display + +### Key Features + +- **Cross-Entity Search**: Search across both conversations and messages simultaneously, or scope to one +- **9 Filter Types**: Audio, Documents, Groups, Photos, Videos, Links, Unread, Conversations, Messages +- **Scoped Search**: Narrow results to a specific user (`uid`) or group (`guid`) +- **Debounced Input**: 500ms debounce prevents excessive SDK calls while typing +- **Lazy-Loaded Sections**: Conversation and message result lists use `@defer` for optimal initial load +- **Unified State Views**: Single empty/error view when both sections report the same state +- **Template Customization**: Override any section of conversation or message result items +- **Real-Time Updates**: Conversation results update with new messages, typing indicators, and user status changes +- **Filter Chip Logic**: Intelligent mutual exclusivity — content filters (Photos, Videos, etc.) are mutually exclusive with Links; conversation filters (Unread, Groups) can coexist + +### Architecture + +``` +CometChatSearchComponent (parent) +├── Search input + filter bar +├── CometChatSearchConversationsListComponent (@defer) +│ ├── Uses SearchConversationsService (signal-based state) +│ └── Renders CometChatConversationItem per result +└── CometChatSearchMessagesListComponent (@defer) + ├── Uses SearchMessagesService (signal-based state) + └── Renders message items with type-specific leading/trailing views +``` + +## Basic Usage + +### Simple Implementation + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatSearchComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-search', + standalone: true, + imports: [CometChatSearchComponent], + template: ` + + + ` +}) +export class SearchComponent { + onConversationClick(event: any): void { + console.log('Conversation:', event.conversation, 'Keyword:', event.searchKeyword); + } + + onMessageClick(event: any): void { + console.log('Message:', event.message, 'Keyword:', event.searchKeyword); + } + + onBack(): void { + // Navigate back + } +} +``` + +### Scoped Search (Within a User or Group) + +```typescript + + + + + + + +``` + + + When `uid` or `guid` is set, conversation filters (Unread, Groups, Conversations) are hidden automatically and only message results are shown. + + +### Scoped to One Entity Type + +```typescript +import { CometChatSearchScope } from '@cometchat/chat-uikit-angular'; + + + + + + + + +``` + + +## Properties + +### Configuration Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `searchIn` | `CometChatSearchScope[]` | `[]` (both) | Scopes to search in. Empty array means both Conversations and Messages | +| `searchFilters` | `CometChatSearchFilter[]` | `[Audio, Documents, Groups, Photos, Videos, Links, Unread]` | Filter chips to display in the filter bar | +| `initialSearchFilter` | `CometChatSearchFilter` | `undefined` | Filter that should be active by default when the component loads | +| `defaultSearchText` | `string` | `undefined` | Pre-fill the search input and immediately trigger a search on load | +| `conversationsRequestBuilder` | `ConversationsRequestBuilder` | `undefined` | Custom request builder for conversation search queries | +| `messagesRequestBuilder` | `MessagesRequestBuilder` | `undefined` | Custom request builder for message search queries | +| `conversationOptions` | `(conv: Conversation) => CometChatOption[]` | `undefined` | Function to provide custom context menu options for conversation results | +| `textFormatters` | `CometChatTextFormatter[]` | `[]` | Custom text formatters for message content display | +| `messageSentAtDateTimeFormat` | `CalendarObject` | `undefined` | Custom date/time format for message timestamps | + +### Display Control Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `hideBackButton` | `boolean` | `false` | Hide the back navigation button in the header | +| `hideGroupType` | `boolean` | `false` | Hide group type icons (public, private, password) in conversation results | +| `hideUserStatus` | `boolean` | `false` | Hide user online/offline status indicator in conversation results | +| `hideReceipts` | `boolean` | `false` | Hide message read receipts (sent, delivered, read) in conversation results | + +### Scoping Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `uid` | `string` | `undefined` | Scope search to messages with a specific user. Hides conversation results and conversation filters | +| `guid` | `string` | `undefined` | Scope search to messages within a specific group. Hides conversation results and conversation filters | + +### Template Properties + +#### Conversation Result Templates + +| Property | Type | Context | Description | +|----------|------|---------|-------------| +| `conversationItemView` | `TemplateRef` | `{ $implicit: Conversation }` | Custom template for the entire conversation result item | +| `conversationLeadingView` | `TemplateRef` | `{ $implicit: Conversation }` | Custom template for the leading section (avatar area) | +| `conversationTitleView` | `TemplateRef` | `{ $implicit: Conversation }` | Custom template for the title section | +| `conversationSubtitleView` | `TemplateRef` | `{ $implicit: Conversation }` | Custom template for the subtitle section (last message preview) | +| `conversationTrailingView` | `TemplateRef` | `{ $implicit: Conversation }` | Custom template for the trailing section (timestamp, badges) | + +#### Message Result Templates + +| Property | Type | Context | Description | +|----------|------|---------|-------------| +| `messageItemView` | `TemplateRef` | `{ $implicit: BaseMessage }` | Custom template for the entire message result item | +| `messageLeadingView` | `TemplateRef` | `{ $implicit: BaseMessage }` | Custom template for the leading section (type-specific icon) | +| `messageTitleView` | `TemplateRef` | `{ $implicit: BaseMessage }` | Custom template for the title section (conversation/sender name) | +| `messageSubtitleView` | `TemplateRef` | `{ $implicit: BaseMessage }` | Custom template for the subtitle section (message content) | +| `messageTrailingView` | `TemplateRef` | `{ $implicit: BaseMessage }` | Custom template for the trailing section (timestamp, image/video thumbnail) | + +#### State View Templates + +| Property | Type | Description | +|----------|------|-------------| +| `initialView` | `TemplateRef` | Shown before any search is performed (no keyword, no filters) | +| `loadingView` | `TemplateRef` | Shown while search results are being fetched (shimmer placeholders by default) | +| `emptyView` | `TemplateRef` | Shown when search returns no results | +| `errorView` | `TemplateRef` | Shown when a search operation fails | + + + Template resolution follows a priority chain: `@Input` template → `CometChatTemplatesService` search template → `CometChatTemplatesService` shared template → built-in default. + + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `backClick` | `void` | Emitted when the back button is clicked | +| `conversationClick` | `SearchConversationClickEvent` | Emitted when a conversation result is clicked. Payload includes `conversation` and `searchKeyword` | +| `messageClick` | `SearchMessageClickEvent` | Emitted when a message result is clicked. Payload includes `message` and `searchKeyword` | +| `searchError` | `CometChat.CometChatException` | Emitted when a search operation fails in either section | + +### Event Payload Types + +```typescript +interface SearchConversationClickEvent { + conversation: CometChat.Conversation; + searchKeyword: string; +} + +interface SearchMessageClickEvent { + message: CometChat.BaseMessage; + searchKeyword: string; +} +``` + + +## Filter System + +The search component includes a filter chip bar that allows users to narrow results by type. Filters are organized into two categories: + +### Conversation Filters + +These filters affect the conversation results section: + +| Filter | Enum Value | Behavior | +|--------|-----------|----------| +| Unread | `CometChatSearchFilter.Unread` | Shows only conversations with unread messages | +| Groups | `CometChatSearchFilter.Groups` | Shows only group conversations | +| Conversations | `CometChatSearchFilter.Conversations` | Shows all conversations (explicit scope) | + +### Message Filters + +These filters affect the message results section: + +| Filter | Enum Value | Behavior | +|--------|-----------|----------| +| Photos | `CometChatSearchFilter.Photos` | Shows only image messages | +| Videos | `CometChatSearchFilter.Videos` | Shows only video messages | +| Documents | `CometChatSearchFilter.Documents` | Shows only file/document messages | +| Audio | `CometChatSearchFilter.Audio` | Shows only audio messages | +| Links | `CometChatSearchFilter.Links` | Shows only messages containing links | +| Messages | `CometChatSearchFilter.Messages` | Shows all message types (explicit scope) | + +### Filter Behavior Rules + +- When a conversation filter is active, only the conversations section renders +- When a message filter is active, only the messages section renders +- Content filters (Photos, Videos, Documents, Audio) are mutually exclusive with Links +- Multiple conversation filters can coexist (e.g., Unread + Groups) +- When no filters are active and a keyword is entered, both sections render +- When `uid` or `guid` is set, conversation filters are hidden automatically + +### Limiting Available Filters + +```typescript +import { CometChatSearchFilter } from '@cometchat/chat-uikit-angular'; + + + + +``` + +### Pre-Selecting a Filter + +```typescript + + +``` + +## Unified State Management + +When both conversation and message sections are active, the component coordinates their empty/error states to avoid duplicate views: + +| Conversations State | Messages State | Result | +|-------------------|---------------|--------| +| Empty | Empty | Single unified empty view (no section headers) | +| Empty | Loaded | Conversations section hidden entirely; messages shown normally | +| Loaded | Empty | Messages section hidden entirely; conversations shown normally | +| Error | Error | Single unified error view (no section headers) | +| Error | Loaded | Conversations shows error with header; messages shown normally | +| Loaded | Error | Conversations shown normally; messages shows error with header | + +When only one scope is active (via `searchIn`, `uid`, or `guid`), that section handles its own empty/error states independently. + + +## Advanced Usage + +### Custom Request Builders + +Override the default SDK query builders for conversations and messages: + +```typescript expandable +import { Component, OnInit } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatSearchComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-custom-search', + standalone: true, + imports: [CometChatSearchComponent], + template: ` + + + ` +}) +export class CustomSearchComponent implements OnInit { + convBuilder!: CometChat.ConversationsRequestBuilder; + msgBuilder!: CometChat.MessagesRequestBuilder; + + ngOnInit(): void { + this.convBuilder = new CometChat.ConversationsRequestBuilder() + .setLimit(15) + .withTags(true); + + this.msgBuilder = new CometChat.MessagesRequestBuilder() + .setLimit(20); + } + + onConversationClick(event: any): void { + // Navigate to conversation + } + + onMessageClick(event: any): void { + // Navigate to message in context + } +} +``` + +### Custom Context Menu for Conversation Results + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatSearchComponent, CometChatOption } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-search-with-menu', + standalone: true, + imports: [CometChatSearchComponent], + template: ` + + + ` +}) +export class SearchWithMenuComponent { + getOptions = (conversation: CometChat.Conversation): CometChatOption[] => { + return [ + { + id: 'pin', + title: 'Pin Conversation', + iconURL: 'assets/pin.svg', + onClick: () => console.log('Pin:', conversation) + }, + { + id: 'delete', + title: 'Delete', + iconURL: 'assets/delete.svg', + onClick: () => console.log('Delete:', conversation) + } + ]; + }; + + onConversationClick(event: any): void { + console.log('Selected:', event.conversation); + } +} +``` + +### Handling Search Results for Navigation + +A common pattern is navigating to the matched message within its conversation when a message result is clicked: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatSearchComponent, + SearchConversationClickEvent, + SearchMessageClickEvent, + ChatStateService, +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-search-navigation', + standalone: true, + imports: [CometChatSearchComponent], + template: ` + + + ` +}) +export class SearchNavigationComponent { + constructor(private chatState: ChatStateService) {} + + onConversationClick(event: SearchConversationClickEvent): void { + const conversationWith = event.conversation.getConversationWith(); + if (conversationWith instanceof CometChat.User) { + this.chatState.setActiveUser(conversationWith); + } else if (conversationWith instanceof CometChat.Group) { + this.chatState.setActiveGroup(conversationWith as CometChat.Group); + } + this.closeSearch(); + } + + onMessageClick(event: SearchMessageClickEvent): void { + // Navigate to the message's conversation and scroll to the message + const message = event.message; + const receiverType = message.getReceiverType(); + + if (receiverType === 'user') { + this.chatState.setActiveUser(message.getSender()); + } else { + this.chatState.setActiveGroup(message.getReceiver() as CometChat.Group); + } + // Your app can use message.getId() to scroll to the specific message + this.closeSearch(); + } + + closeSearch(): void { + // Navigate back to the main chat view + } +} +``` + +## Customization with Templates + +### Custom Message Result Item + +Override the entire message result row: + +```typescript expandable +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatSearchComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-custom-message-results', + standalone: true, + imports: [CommonModule, CometChatSearchComponent], + template: ` + + + +
+
+ {{ message.getType() | uppercase }} +
+
+ {{ message.getSender()?.getName() }} + {{ getPreview(message) }} +
+ {{ message.getSentAt() * 1000 | date:'shortTime' }} +
+
+
+ `, + styles: [` + .custom-message-row { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + cursor: pointer; + } + .custom-message-row:hover { + background: var(--cometchat-background-color-02); + } + .message-type-badge { + background: var(--cometchat-primary-color); + color: white; + padding: 4px 8px; + border-radius: 4px; + font-size: 10px; + font-weight: 600; + } + .message-body { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; + } + .sender { + font: var(--cometchat-font-body-medium); + color: var(--cometchat-text-color-primary); + } + .content { + font: var(--cometchat-font-body-regular); + color: var(--cometchat-text-color-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .time { + font: var(--cometchat-font-caption1-regular); + color: var(--cometchat-text-color-secondary); + } + `] +}) +export class CustomMessageResultsComponent { + getPreview(message: CometChat.BaseMessage): string { + if (message.getType() === 'text') { + return (message as CometChat.TextMessage).getText(); + } + return message.getType(); + } + + onMessageClick(event: any): void { + console.log('Message clicked:', event); + } +} +``` + +### Custom Empty and Error States + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatSearchComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-custom-states', + standalone: true, + imports: [CometChatSearchComponent], + template: ` + + + +
+ No results +

Nothing found

+

Try a different keyword or filter

+
+
+ + +
+ Error +

Search failed

+

Please check your connection and try again

+
+
+
+ ` +}) +export class CustomStatesComponent {} +``` + +### Custom Initial View + +Replace the default "Search for messages, conversations..." placeholder: + +```typescript + + +
+

Find anything

+

Search across all your conversations and messages

+
+
+
+``` + + +## Keyboard Accessibility + +CometChatSearch provides keyboard accessibility for the search input, filter bar, and result items. + +### Keyboard Shortcuts + +| Key | Action | Context | +|-----|--------|---------| +| `Tab` | Navigate between search input, filter chips, and result items | Global | +| `Shift + Tab` | Navigate backwards | Global | +| `Enter` | Activate focused result item or filter chip | When focused | +| `Space` | Toggle filter chip or activate result item | When focused | +| `Escape` | Clear search text and active filters | When search input is focused | + +### Accessibility Features + +**ARIA Attributes:** +- `role="search"` on the main container +- `role="searchbox"` on the search input +- `role="toolbar"` on the filter bar +- `aria-pressed` on filter chips indicating active state +- `aria-label` on back button, search input, and clear button +- `aria-live="assertive"` on empty and error state regions +- `role="list"` and `role="listitem"` on result sections + +**Screen Reader Support:** +- Filter chip labels are localized via the translate pipe +- State changes (empty, error) are announced via `aria-live` regions +- Back button and clear button have descriptive `aria-label` attributes + +**Focus Management:** +- Search input auto-focuses on component load +- Clear button restores focus to search input after clearing +- Result items are focusable with `tabindex="0"` (message results) +- Visible focus indicators meeting WCAG contrast requirements + +## Styling with CSS Variables + +The component uses CometChat CSS variables for theming. Key variables: + +```css +cometchat-search { + /* Background */ + --cometchat-background-color-01: #ffffff; + --cometchat-background-color-02: #f5f5f5; + --cometchat-background-color-03: #eeeeee; + + /* Text */ + --cometchat-text-color-primary: #141414; + --cometchat-text-color-secondary: #727272; + --cometchat-text-color-tertiary: #a1a1a1; + + /* Primary color (active filter, icons) */ + --cometchat-primary-color: #6852D6; + + /* Border */ + --cometchat-border-color-dark: #dcdcdc; + --cometchat-border-color-light: #e8e8e8; + + /* Icons */ + --cometchat-icon-color-primary: #141414; + --cometchat-icon-color-secondary: #a1a1a1; + + /* Typography */ + --cometchat-font-heading4-regular: 400 16px/24px sans-serif; + --cometchat-font-body-regular: 400 14px/20px sans-serif; + --cometchat-font-body-medium: 500 14px/20px sans-serif; + --cometchat-font-caption1-regular: 400 12px/16px sans-serif; + --cometchat-font-caption1-medium: 500 12px/16px sans-serif; + + /* Spacing */ + --cometchat-padding-2: 8px; + --cometchat-padding-3: 12px; + --cometchat-padding-4: 16px; + + /* Border radius */ + --cometchat-radius-max: 1000px; + --cometchat-radius-2: 8px; +} +``` + +### Filter Chip Styling + +Active filter chips use a dark pill style with white text and icons: + +```css +/* Default filter chip */ +.cometchat-search__body-filter { + background: var(--cometchat-background-color-03); + color: var(--cometchat-text-color-secondary); + border-radius: var(--cometchat-radius-max); +} + +/* Active filter chip */ +.cometchat-search__body-filter-active { + background: var(--cometchat-neutral-color-900); + color: var(--cometchat-neutral-color-50); + border: 1px solid var(--cometchat-neutral-color-800); +} +``` + +## Error Handling + +### Built-in Error Handling + +The component handles errors from both conversation and message search services: + +```typescript + + +``` + +```typescript expandable +handleSearchError(error: CometChat.CometChatException): void { + console.error('Search error:', error); + + if (error.code === 'NETWORK_ERROR') { + this.showToast('Network error. Please check your connection.'); + } else if (error.code === 'AUTH_ERROR') { + this.showToast('Authentication error. Please log in again.'); + } else { + this.showToast('Search failed. Please try again.'); + } +} +``` + +### Error State Behavior + +- If only one section errors while the other has results, the errored section shows its own error view with its section header +- If both sections error, a single unified error view is shown without section headers +- Custom `errorView` templates are used when provided + +## Complete Example + +```typescript expandable +import { Component, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatSearchComponent, + CometChatSearchFilter, + CometChatSearchScope, + SearchConversationClickEvent, + SearchMessageClickEvent, + ChatStateService, + CometChatOption, +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-search-demo', + standalone: true, + imports: [CommonModule, CometChatSearchComponent], + template: ` +
+ + + +
+

No results

+

Try a different search term or filter

+
+
+
+
+ `, + styles: [` + .search-container { + height: 100vh; + width: 400px; + } + `] +}) +export class SearchDemoComponent implements OnInit { + filters = [ + CometChatSearchFilter.Unread, + CometChatSearchFilter.Groups, + CometChatSearchFilter.Photos, + CometChatSearchFilter.Videos, + CometChatSearchFilter.Documents, + CometChatSearchFilter.Audio, + CometChatSearchFilter.Links, + ]; + + constructor(private chatState: ChatStateService) {} + + ngOnInit(): void {} + + getConversationOptions = (conv: CometChat.Conversation): CometChatOption[] => { + return [ + { id: 'delete', title: 'Delete', iconURL: 'assets/delete.svg', onClick: () => console.log('Delete:', conv) }, + ]; + }; + + onConversationClick(event: SearchConversationClickEvent): void { + const entity = event.conversation.getConversationWith(); + if (entity instanceof CometChat.User) { + this.chatState.setActiveUser(entity); + } else { + this.chatState.setActiveGroup(entity as CometChat.Group); + } + this.closeSearch(); + } + + onMessageClick(event: SearchMessageClickEvent): void { + const msg = event.message; + if (msg.getReceiverType() === 'user') { + this.chatState.setActiveUser(msg.getSender()); + } else { + this.chatState.setActiveGroup(msg.getReceiver() as CometChat.Group); + } + this.closeSearch(); + } + + onError(error: CometChat.CometChatException): void { + console.error('Search error:', error); + } + + closeSearch(): void { + // Navigate back to main chat view + } +} +``` + +## Related Components + +- **CometChatConversations**: Full conversation list component (CometChatSearch uses `CometChatConversationItem` internally for conversation results) +- **CometChatSearchBar**: Standalone search bar component (CometChatSearch has its own built-in search input) +- **CometChatMessageList**: Message list component for displaying messages in a conversation +- **CometChatMessageHeader**: Header component that pairs with search for navigation context + +## Technical Details + +- **Standalone Component**: Can be imported and used independently +- **Change Detection**: Uses `OnPush` strategy for optimal performance +- **Lazy Loading**: Child result components use `@defer` blocks for code splitting +- **Signal-Based State**: Services use Angular signals for reactive state management +- **Debounced Search**: 500ms debounce on the search input to reduce SDK calls +- **BEM CSS**: Follows Block Element Modifier naming convention (`cometchat-search__*`) +- **Localization**: All text uses the `translate` pipe for i18n support + +--- + +## Related Documentation + +- [SearchConversationsService](/ui-kit/angular/v5/api-reference/search-conversations-service) — Conversation search state, SDK queries, real-time listeners +- [SearchMessagesService](/ui-kit/angular/v5/api-reference/search-messages-service) — Message search state, attachment-type filtering, scoped queries +- [CometChatTemplatesService](/ui-kit/angular/v5/api-reference/templates-service) — Template resolution priority chain and global template overrides +- [Accessibility](/accessibility#search) — Cross-component keyboard accessibility reference +- [Events](/events) — UIKit event bus for decoupled component communication diff --git a/ui-kit/angular/v5/components/cometchat-thread-view.mdx b/ui-kit/angular/v5/components/cometchat-thread-view.mdx new file mode 100644 index 000000000..9dded1e76 --- /dev/null +++ b/ui-kit/angular/v5/components/cometchat-thread-view.mdx @@ -0,0 +1,162 @@ +--- +title: "CometChatThreadView" +description: "A component for displaying thread reply indicators on messages and serving as an accessible thread panel container" +--- + +## Overview + +The CometChatThreadView component displays thread reply indicators on messages and can also serve as an accessible container for thread content. It supports two modes of operation. + +### Key Features + +- **Indicator Mode**: Shows reply count with thread icon on messages that have replies +- **Panel Mode**: Serves as an accessible container for thread content with focus management +- **Unread Indicator**: Visual indicator when there are unread replies in the thread +- **Keyboard Navigation**: Enter/Space to open thread, Escape to close (panel mode) +- **Screen Reader Support**: Announces thread opened with reply count +- **Localization**: Singular/plural reply count text with localization support +- **Overflow Handling**: Shows "999+" for threads with more than 999 replies + +## Basic Usage + +### Indicator Mode (Default) + +Shows a clickable reply count indicator on messages with thread replies: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatThreadViewComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-thread-indicator', + standalone: true, + imports: [CometChatThreadViewComponent], + template: ` + + + ` +}) +export class ThreadIndicatorComponent { + message!: CometChat.BaseMessage; + + onThreadClick(parentMessage: CometChat.BaseMessage): void { + console.log('Open thread for message:', parentMessage.getId()); + } +} +``` + +### Panel Mode + +Serves as an accessible container for thread content with focus trapping and Escape-to-close: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatThreadViewComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-thread-panel', + standalone: true, + imports: [CometChatThreadViewComponent], + template: ` + + + + ` +}) +export class ThreadPanelComponent { + parentMessage!: CometChat.BaseMessage; + replyCount = 12; + + onCloseThread(): void { + // Close the thread panel + } +} +``` + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `mode` | `'indicator' \| 'panel'` | `'indicator'` | The display mode. `indicator` shows reply count on messages; `panel` serves as an accessible thread container. | +| `replyCount` | `number` | `0` | The number of replies in the thread. Component only displays when `replyCount > 0` in indicator mode. | +| `unreadReplyCount` | `number` | `0` | The number of unread replies. Shows an unread indicator dot when greater than 0. | +| `parentMessage` | `CometChat.BaseMessage` | `undefined` | The parent message for the thread. Emitted with `threadClick` event. | +| `announceOnOpen` | `boolean` | `true` | Whether to announce "Thread opened with X replies" via screen reader when panel mode initializes. | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `threadClick` | `CometChat.BaseMessage` | Emitted when the thread indicator is clicked (indicator mode only). Payload is the parent message. | +| `closeClick` | `void` | Emitted when the thread should be closed (panel mode). Triggered by Escape key. | + +## Keyboard Accessibility + +### Indicator Mode + +| Key | Action | +|-----|--------| +| `Tab` | Focus the thread indicator | +| `Enter` / `Space` | Open the thread (emits `threadClick`) | + +### Panel Mode + +| Key | Action | +|-----|--------| +| `Escape` | Close the thread panel (emits `closeClick`) and restore focus | + +### Accessibility Features + +- **Indicator mode**: `role="button"`, `tabindex="0"`, descriptive `aria-label` with reply count and unread status +- **Panel mode**: `role="complementary"`, `aria-label="Thread replies"` +- Focus is restored to the previously focused element when panel closes +- Thread opened announcement via `aria-live` region (configurable via `announceOnOpen`) + +## Display Logic + +| Condition | Behavior | +|-----------|----------| +| `replyCount === 0` | Component is hidden (indicator mode) | +| `replyCount === 1` | Shows "1 Reply" | +| `replyCount > 1` | Shows "{count} Replies" | +| `replyCount > 999` | Shows "999+ Replies" | +| `unreadReplyCount > 0` | Shows unread indicator dot | + +## Localization Keys + +| Key | Default (en-us) | +|-----|-----------------| +| `thread_reply` | "Reply" | +| `thread_replies` | "Replies" | +| `accessibility_thread_replies` | "Thread replies" | +| `accessibility_thread_opened` | "Thread opened with {count} replies" | + +## CSS Variables + +```css +cometchat-thread-view { + --cometchat-text-color-primary: #141414; + --cometchat-text-color-secondary: #727272; + --cometchat-primary-color: #6852D6; + --cometchat-icon-color-secondary: #a1a1a1; + --cometchat-font-caption1-medium: 500 12px/16px sans-serif; + --cometchat-spacing-1: 4px; + --cometchat-spacing-2: 8px; +} +``` + +## Related Components + +- [CometChatMessageBubble](/ui-kit/angular/v5/components/cometchat-message-bubble) — Renders the thread view indicator within message bubbles +- [CometChatThreadHeader](/ui-kit/angular/v5/components/cometchat-thread-header) — Header component for the thread panel +- [CometChatMessageList](/ui-kit/angular/v5/components/cometchat-message-list) — Displays messages within the thread panel diff --git a/ui-kit/angular/v5/customization/migration-guide.mdx b/ui-kit/angular/v5/customization/migration-guide.mdx new file mode 100644 index 000000000..93e0814f9 --- /dev/null +++ b/ui-kit/angular/v5/customization/migration-guide.mdx @@ -0,0 +1,1499 @@ +--- +title: "Migration Guide" +description: "Complete guide for migrating from CometChat Angular UIKit v4 to v5, including breaking changes, migration steps, and new features" +--- + +## Overview + +This guide covers migration from CometChat Angular UIKit v4 to v5, as well as enterprise refactoring of specific components. The v5 UIKit introduces a completely redesigned architecture with standalone components, CSS variables for theming, and improved customization options. + +--- + +# Part 1: Migrating from v4 to v5 + +This section covers the breaking changes and migration steps when upgrading from CometChat Angular UIKit v4 to v5. + + + **Breaking Changes Alert!** The v5 UIKit introduces significant architectural changes. While the component APIs remain similar, the underlying architecture has changed substantially. Please review all breaking changes before upgrading. + + +## What's Changed in v5 + +The v5 Angular UIKit has been completely redesigned with the following key changes: + +| Area | v4 | v5 | +|------|----|----| +| **Component Architecture** | Module-based components | Standalone components (`standalone: true`) | +| **Theming** | Style objects and configuration classes | CSS variables with BEM naming | +| **Localization** | External library (ngx-translate) | Built-in CometChatLocalize service | +| **State Management** | Props-only approach | Hybrid approach (ChatStateService + props) | +| **Template Customization** | Direct template inputs | MessageBubbleConfigService + template inputs | +| **Event Naming** | Various patterns (`onItemClick`, etc.) | Consistent pattern without 'on' prefix | +| **Imports** | Import from module | Import individual standalone components | + +## Breaking Changes + +### 1. Component Architecture: Module-Based to Standalone + +**v4 (Module-based):** +```typescript +// v4: Import module +import { CometChatUIKitModule } from '@cometchat/chat-uikit-angular'; + +@NgModule({ + imports: [CometChatUIKitModule], + // ... +}) +export class AppModule {} +``` + +**v5 (Standalone):** +```typescript expandable +// v5: Import individual standalone components +import { + CometChatMessageListComponent, + CometChatMessageHeaderComponent, + CometChatMessageComposerComponent +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-chat', + standalone: true, + imports: [ + CometChatMessageListComponent, + CometChatMessageHeaderComponent, + CometChatMessageComposerComponent + ], + template: `...` +}) +export class ChatComponent {} +``` + +**Migration Steps:** +1. Remove `CometChatUIKitModule` from your `NgModule` imports +2. Convert your components to standalone (recommended) or import individual components in your module +3. Import only the components you need in each component's `imports` array + +### 2. Component Selector Changes + +All component selectors now use a consistent `cometchat-*` prefix: + +| v4 Selector | v5 Selector | +|-------------|-------------| +| `cometchat-message-list` | `cometchat-message-list` (unchanged) | +| `cometchat-message-header` | `cometchat-message-header` (unchanged) | +| `cometchat-conversations` | `cometchat-conversations` (unchanged) | +| `cometchat-users` | `cometchat-users` (unchanged) | +| `cometchat-groups` | `cometchat-groups` (unchanged) | + + + Component selectors remain largely unchanged in v5. The main difference is that components are now standalone and must be imported individually. + + +### 3. Styling: Style Objects to CSS Variables + +**v4 (Style Objects):** +```typescript expandable +// v4: Using style configuration objects +import { MessageListStyle, AvatarStyle } from '@cometchat/chat-uikit-angular'; + +@Component({ + template: ` + + + ` +}) +export class ChatComponent { + messageListStyle: MessageListStyle = { + background: '#f5f5f5', + border: '1px solid #e0e0e0', + borderRadius: '8px', + height: '100%', + width: '100%' + }; + + avatarStyle: AvatarStyle = { + borderRadius: '50%', + width: '40px', + height: '40px' + }; +} +``` + +**v5 (CSS Variables):** +```css expandable +/* v5: Using CSS variables */ +@import '@cometchat/chat-uikit-angular/styles/css-variables.css'; + +/* Global theming */ +:root { + --cometchat-primary-color: #6852D6; + --cometchat-background-color-01: #ffffff; + --cometchat-background-color-02: #f5f5f5; + --cometchat-text-color-primary: #141414; + --cometchat-text-color-secondary: #727272; +} + +/* Component-specific styling */ +cometchat-message-list { + --cometchat-message-list-background: var(--cometchat-background-color-01); + --cometchat-message-list-border-radius: var(--cometchat-radius-2); +} + +/* Dark theme */ +[data-theme="dark"] { + --cometchat-background-color-01: #1a1a1a; + --cometchat-background-color-02: #2a2a2a; + --cometchat-text-color-primary: #ffffff; +} +``` + +```typescript expandable +// v5: No style props needed in component +@Component({ + template: ` + + + ` +}) +export class ChatComponent { + user?: CometChat.User; +} +``` + +**Migration Steps:** +1. Remove all `*Style` input properties from your templates +2. Import the CSS variables file in your global styles +3. Override CSS variables to customize appearance +4. Use `data-theme` attribute for dark mode switching + +### 4. Localization: External Library to Built-in Service + +**v4 (ngx-translate or similar):** +```typescript expandable +// v4: Using external translation library +import { TranslateModule, TranslateService } from '@ngx-translate/core'; + +@NgModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + }) + ] +}) +export class AppModule {} + +// In component +constructor(private translate: TranslateService) { + translate.setDefaultLang('en'); +} +``` + +**v5 (Built-in CometChatLocalize):** +```typescript expandable +// v5: Using built-in localization +import { CometChatLocalize } from '@cometchat/chat-uikit-angular'; + +// Set language +CometChatLocalize.setCurrentLanguage('en'); + +// Add custom translations — pass a record keyed by language code +CometChatLocalize.addTranslation({ + 'en-US': { + 'custom_key': 'Custom Value' + } +}); + +// In templates, use the translate pipe +@Component({ + template: ` + {{ 'SEND_MESSAGE' | translate }} + ` +}) +export class ChatComponent {} +``` + +**Migration Steps:** +1. Remove ngx-translate or other translation library dependencies +2. Use `CometChatLocalize.setCurrentLanguage()` to set the active language +3. Use the built-in `translate` pipe in templates +4. Add custom translations using `CometChatLocalize.addTranslation()` + +### 5. State Management: Props-Only to Hybrid Approach + +**v4 (Props-only):** +```typescript expandable +// v4: Pass user/group to every component +@Component({ + template: ` + + + + ` +}) +export class ChatComponent { + selectedUser?: CometChat.User; + + onUserSelect(user: CometChat.User): void { + this.selectedUser = user; + } +} +``` + +**v5 (Hybrid with ChatStateService):** +```typescript expandable +// v5: Use ChatStateService for shared state +import { ChatStateService } from '@cometchat/chat-uikit-angular'; + +@Component({ + template: ` + + + + + ` +}) +export class ChatComponent { + constructor(private chatState: ChatStateService) {} + + onUserSelect(user: CometChat.User): void { + // Set once, all components react + this.chatState.setActiveUser(user); + } +} + +// OR: Props still work and take priority +@Component({ + template: ` + + + ` +}) +export class OverrideComponent { + specificUser?: CometChat.User; +} +``` + +**Migration Steps:** +1. Inject `ChatStateService` in your components +2. Use `setActiveUser()`, `setActiveGroup()`, or `setActiveConversation()` to set shared state +3. Components will automatically react to state changes +4. Use props when you need to override the shared state + +### 6. Event Naming Convention + +**v4 (Various patterns):** +```text +// v4: Inconsistent event naming — used 'on' prefix on Output bindings +// (onItemClick)="handleItemClick($event)" → now (itemClick) +// (onBack)="handleBack()" → now (backClick) +// (onThreadRepliesClick)="handleThreadClick($event)" → now (threadRepliesClick) +``` +``` + +**v5 (Consistent pattern without 'on' prefix):** +```typescript +// v5: Consistent event naming (no 'on' prefix in Output) +@Component({ + template: ` + + + + + + ` +}) +export class ChatComponent { + handleItemClick(conversation: CometChat.Conversation): void {} + handleBack(): void {} + handleThreadClick(message: CometChat.BaseMessage): void {} +} +``` expandable + +**Migration Steps:** +1. Update all event bindings to remove the 'on' prefix +2. `(onItemClick)` → `(itemClick)` +3. `(onBack)` → `(backClick)` +4. `(onThreadRepliesClick)` → `(threadRepliesClick)` +5. `(onError)` → `(error)` + +### 7. Message Template Customization + +**v4 (Direct template inputs):** +```typescript +// v4: Pass templates directly to component +@Component({ + template: ` + + + + + + + ` +}) +export class ChatComponent {} +``` + +**v5 (MessageBubbleConfigService + template inputs):** +```typescript +// v5: Use MessageBubbleConfigService for SDK-wide templates +import { MessageBubbleConfigService } from '@cometchat/chat-uikit-angular'; + +@Component({ + template: ` + + + + + +
{{ context.message.getText() }}
+
+ ` +}) +export class ChatComponent implements AfterViewInit { + @ViewChild('customTextContent') customTextContent!: TemplateRef; + + constructor(private bubbleConfig: MessageBubbleConfigService) {} + + ngAfterViewInit(): void { + + this.bubbleConfig.setBubbleView('message_text', { + contentView: this.customTextContent + }); + } +} +``` expandable + +**Migration Steps:** +1. Update template property names to match v5 API +2. Use `MessageBubbleConfigService` for SDK-wide template customization +4. Update template context variables to match new structure + +### 8. Removed Properties + +The following style-related properties have been removed in v5. Use CSS variables instead: + +| Removed Property | CSS Variable Alternative | +|------------------|-------------------------| +| `messageListStyle` | `--cometchat-message-list-*` variables | +| `avatarStyle` | `--cometchat-avatar-*` variables | +| `listItemStyle` | `--cometchat-list-item-*` variables | +| `badgeStyle` | `--cometchat-badge-*` variables | +| `receiptStyle` | `--cometchat-receipt-*` variables | +| `dateStyle` | `--cometchat-date-*` variables | +| `statusIndicatorStyle` | `--cometchat-status-*` variables | + +### 9. Removed Configuration Properties + +| Removed Property | v5 Alternative | +|------------------|----------------| +| `title` | Use localization: `{{ 'component_title' \| translate }}` | +| `emptyStateText` | Use `emptyView` template | +| `errorStateText` | Use `errorView` template | +| `loadingIconURL` | Use `loadingView` template | +| `searchPlaceholderText` | Use localization | +| `hideSeparator` | Use CSS: `border-bottom: none` | + +## Message List Specific Changes + +### New Properties in v5 + +| Property | Type | Description | +|----------|------|-------------| +| `hideStickyDate` | `boolean` | Hide the sticky date separator | +| `hideReplyInThreadOption` | `boolean` | Hide Reply In Thread option | +| `hideTranslateMessageOption` | `boolean` | Hide Translate Message option | +| `hideEditMessageOption` | `boolean` | Hide Edit Message option | +| `hideDeleteMessageOption` | `boolean` | Hide Delete Message option | +| `hideReactionOption` | `boolean` | Hide React option | +| `hideCopyMessageOption` | `boolean` | Hide Copy option | +| `hideMessageInfoOption` | `boolean` | Hide Message Info option | +| `hideGroupActionMessages` | `boolean` | Hide group action messages | +| `showConversationStarters` | `boolean` | Show AI conversation starters | +| `showSmartReplies` | `boolean` | Show AI smart replies | +| `quickOptionsCount` | `number` | Number of quick options on bubble | +| `reactionsRequestBuilder` | `ReactionsRequestBuilder` | Custom reactions request builder | + +### Renamed Properties + +| v4 Property | v5 Property | +|-------------|-------------| +| `disableReceipt` | `hideReceipts` | +| `disableSoundForMessages` | `disableSoundForMessages` (unchanged) | +| `customSoundForMessages` | `customSoundForMessages` (unchanged) | + +### Event Changes + +| v4 Event | v5 Event | +|----------|----------| +| `onThreadRepliesClick` | `threadRepliesClick` | +| `onError` | `error` | + +## New Features in v5 + +### 1. Standalone Components +All components are now standalone, enabling tree-shaking and smaller bundle sizes. + +### 2. CSS Variables Theming +Over 100+ CSS variables for comprehensive theming without code changes. + +### 3. Built-in Localization +Zero-dependency localization with runtime language switching. + +### 4. ChatStateService +Centralized state management for active conversation, user, and group. + +### 5. MessageBubbleConfigService +SDK-wide message template customization. + +### 6. Enhanced Accessibility +Full keyboard navigation and ARIA support. + +### 7. AI Smart Chat Features +Built-in support for Smart Replies and Conversation Starters. + +### 8. Reactions +Native support for message reactions with customizable UI. + +## Quick Migration Checklist + +- [ ] Update package to v5: `npm install @cometchat/chat-uikit-angular@5` +- [ ] Convert components to standalone or update module imports +- [ ] Import CSS variables in global styles +- [ ] Remove all `*Style` input properties +- [ ] Replace style objects with CSS variable overrides +- [ ] Update event bindings (remove 'on' prefix) +- [ ] Replace ngx-translate with built-in localization +- [ ] Update template customization to use new API +- [ ] Test dark mode with `data-theme="dark"` attribute +- [ ] Verify all functionality works as expected + +--- + +# Part 2: Enterprise Refactoring (Conversations Component) + +The following section covers the enterprise refactoring of the CometChatConversations component. This refactoring introduces powerful new features while maintaining **full backward compatibility** - your existing code will continue to work without any changes. + + + **Good News!** This is a non-breaking upgrade. All existing implementations will continue to work exactly as before. The new features are additive enhancements that you can adopt incrementally. + + +### What's New + +The enterprise refactoring introduces: + +1. **CometChatConversationItem** - A standalone component for rendering single conversation items +2. **CometChatPaginatedList<T>** - A generic reusable infinite scroll component +3. **CometChatTemplatesService** - SDK-wide template customization service +4. **Slot-Based Customization** - 11 fine-grained customization slots +5. **Granular Events** - Separate events for avatar, title, subtitle, timestamp, and badge clicks +6. **108 CSS Variables** - Comprehensive theming without code changes + +### Migration Scenarios + +| Scenario | Action Required | +|----------|-----------------| +| Using default CometChatConversations | **None** - Works as-is | +| Using template @Inputs (itemView, leadingView, etc.) | **None** - Works as-is | +| Using ContentChild templates | **None** - Works as-is | +| Want SDK-wide template customization | **Optional** - Use CometChatTemplatesService | +| Want granular element customization | **Optional** - Use slot-based customization | +| Want specific element click handling | **Optional** - Use new granular events | +| Want comprehensive theming | **Optional** - Use new CSS variables | + +## No Changes Required + +If you're using CometChatConversations with any of the following patterns, **no changes are required**: + +### Basic Usage (No Changes) + +```typescript +// ✅ This continues to work exactly as before + +``` + + +### With Configuration Properties (No Changes) + +```typescript +// ✅ All existing @Input properties continue to work + +``` + +### With Template Customization (No Changes) + +```typescript +// ✅ All existing template @Inputs continue to work + + + + + +``` + +### With ContentChild Templates (No Changes) + +```typescript +// ✅ ContentChild template projection continues to work + + +
+ {{ getCustomSubtitle(conversation) }} +
+
+
+``` expandable + +## New Feature: CometChatTemplatesService + +The `CometChatTemplatesService` enables SDK-wide template customization. Templates registered with this service apply to **all** CometChatConversations instances in your application. + +### Template Priority + +Templates are resolved in the following priority order: + +1. **Component @Input** (highest priority) +2. **ContentChild template** +3. **CometChatTemplatesService** (SDK-wide) +4. **Default template** (lowest priority) + +### Before: Per-Instance Customization + +```typescript +// Before: You had to customize each instance separately +@Component({ + template: ` + + + + {{ getCustomSubtitle(conversation) }} + + + + + + + {{ getCustomSubtitle(conversation) }} + + + ` +}) +export class BeforeComponent {} +``` + +### After: SDK-Wide Customization + +```typescript +// After: Register once, applies everywhere +import { Component, ViewChild, TemplateRef, AfterViewInit } from '@angular/core'; +import { CometChatTemplatesService } from '@cometchat/chat-uikit-angular'; + +@Component({ + template: ` + + + {{ getCustomSubtitle(conversation) }} + + + + + + + + + + + + Special: {{ conversation.getConversationWith().getName() }} + + + ` +}) +export class AfterComponent implements AfterViewInit { + @ViewChild('globalSubtitle') globalSubtitle!: TemplateRef; + + constructor(private templatesService: CometChatTemplatesService) {} + + ngAfterViewInit(): void { + // Register template SDK-wide + this.templatesService.setConversationSubtitleTemplate(this.globalSubtitle); + } + + getCustomSubtitle(conversation: any): string { + // Your custom logic + return 'Custom subtitle'; + } +} +``` expandable + + +### Available Template Methods + +The `CometChatTemplatesService` provides methods for shared (cross-cutting) and component-specific template customization. + +#### Shared Templates (All Lists) + +| Method | Description | +|--------|-------------| +| `setSharedTemplates(templates)` | Set loading/empty/error templates that apply to **all** list components as a fallback | +| `getSharedTemplates()` | Get the current shared templates | +| `clearSharedTemplates()` | Clear all shared templates | + +```typescript +// Example: Branded loading spinner for every list in the app +this.templatesService.setSharedTemplates({ + loadingView: this.brandedSpinner, + errorView: this.brandedError, +}); +``` expandable + +#### Conversation Templates + +| Method | Description | +|--------|-------------| +| `setConversationTemplates(templates)` | Set multiple conversation templates at once | +| `setConversationItemTemplate(template)` | Set template for entire conversation item | +| `setConversationLeadingTemplate(template)` | Set template for leading section (avatar area) | +| `setConversationTitleTemplate(template)` | Set template for title section | +| `setConversationSubtitleTemplate(template)` | Set template for subtitle section | +| `setConversationTrailingTemplate(template)` | Set template for trailing section | +| `setConversationLoadingTemplate(template)` | Set template for loading state | +| `setConversationEmptyTemplate(template)` | Set template for empty state | +| `setConversationErrorTemplate(template)` | Set template for error state | +| `getConversationTemplates()` | Get current template configuration | +| `clearConversationTemplates()` | Clear all registered templates | + +#### User, Group, GroupMember, CallLog, MessageList Templates + +Each component has `set{Component}Templates()`, `get{Component}Templates()`, and `clear{Component}Templates()` methods: + +| Component | Set Method | Get Method | Clear Method | +|-----------|-----------|------------|--------------| +| Users | `setUserTemplates(templates)` | `getUserTemplates()` | `clearUserTemplates()` | +| Groups | `setGroupTemplates(templates)` | `getGroupTemplates()` | `clearGroupTemplates()` | +| GroupMembers | `setGroupMemberTemplates(templates)` | `getGroupMemberTemplates()` | `clearGroupMemberTemplates()` | +| CallLogs | `setCallLogTemplates(templates)` | `getCallLogTemplates()` | `clearCallLogTemplates()` | +| MessageList | `setMessageListTemplates(templates)` | `getMessageListTemplates()` | `clearMessageListTemplates()` | + +#### Bulk Operations + +| Method | Description | +|--------|-------------| +| `clearAllTemplates()` | Clear all templates (shared + all component-specific) | + +See the full [CometChatTemplatesService API Reference](/ui-kit/angular/v5/api-reference/templates-service) for complete interface definitions and examples. + +### Clearing Templates + +```typescript +// Clear all SDK-wide templates +this.templatesService.clearConversationTemplates(); + +// After clearing, components will use their @Input templates or defaults +``` expandable + +## New Feature: Slot-Based Customization + +Slots provide fine-grained control over individual UI elements within a conversation item. Unlike section templates that replace entire sections, slots let you customize specific elements while keeping default behavior for others. + +### Available Slots (11 Total) + +| Section | Slot Name | Description | +|---------|-----------|-------------| +| **Leading** | `avatar` | Custom avatar element | +| **Leading** | `statusIndicator` | User online/offline status indicator | +| **Leading** | `groupTypeIcon` | Group type icon (public/private/password) | +| **Leading** | `typingIndicator` | Typing animation indicator | +| **Body** | `title` | Conversation title (user/group name) | +| **Body** | `subtitle` | Last message preview | +| **Body** | `subtitleReceipt` | Receipt indicator in subtitle area | +| **Trailing** | `timestamp` | Message timestamp display | +| **Trailing** | `unreadBadge` | Unread message count badge | +| **Trailing** | `receipt` | Message receipt indicator | +| **Trailing** | `contextMenuTrigger` | Context menu trigger button | + +### Before: Section-Level Customization + +```typescript +// Before: Replacing entire leading section to customize just the avatar +@Component({ + template: ` + + + +
+
+ + VIP +
+ + + + + {{ getGroupTypeIcon(conversation) }} + +
+
+
+ ` +}) +export class BeforeSlotComponent {} +``` + +### After: Slot-Based Customization + +```typescript +// After: Customize only the avatar, keep default status and group icons +import { Component, ViewChild, TemplateRef, AfterViewInit } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatConversationsComponent, + ConversationSlots +} from '@cometchat/chat-uikit-angular'; + +@Component({ + template: ` + + + +
+ + VIP +
+
+
+ ` +}) +export class AfterSlotComponent implements AfterViewInit { + @ViewChild('customAvatarSlot') customAvatarSlot!: TemplateRef; + + customSlots: Partial = {}; + + ngAfterViewInit(): void { + // Only override the avatar slot - status indicator and group icon use defaults + this.customSlots = { + avatar: this.customAvatarSlot + }; + } + + getAvatar(conversation: CometChat.Conversation): string { + const conversationWith = conversation.getConversationWith(); + if (conversationWith instanceof CometChat.User) { + return conversationWith.getAvatar() || ''; + } + return (conversationWith as CometChat.Group).getIcon() || ''; + } + + isVIP(conversation: CometChat.Conversation): boolean { + // Your VIP logic + return false; + } +} +``` + + +### Slot Context + +All slot templates receive a `ConversationSlotContext` object with the following properties: + +```typescript +interface ConversationSlotContext { + $implicit: CometChat.Conversation; // Implicit context (let-conversation) + conversation: CometChat.Conversation; // Explicit named variable + isActive: boolean; // Whether conversation is active + isSelected: boolean; // Whether conversation is selected + unreadCount: number; // Number of unread messages + isTyping: boolean; // Whether someone is typing +} +``` expandable + +Some slots receive additional context: + +| Slot | Additional Context | +|------|-------------------| +| `statusIndicator` | `{ status: string }` | +| `groupTypeIcon` | `{ groupType: string }` | +| `title` | `{ title: string }` | +| `subtitle` | `{ subtitle: string }` | +| `timestamp` | `{ timestamp: number }` | +| `unreadBadge` | `{ count: number }` | +| `receipt` | `{ receiptStatus: string }` | + +### Multiple Slots Example + +```typescript +@Component({ + template: ` + + + +
+ {{ count > 99 ? '99+' : count }} +
+
+ + + + {{ getRelativeTime(timestamp) }} + +
+ ` +}) +export class MultiSlotComponent implements AfterViewInit { + @ViewChild('customBadge') customBadge!: TemplateRef; + @ViewChild('customTimestamp') customTimestamp!: TemplateRef; + + customSlots: Partial = {}; + + ngAfterViewInit(): void { + this.customSlots = { + unreadBadge: this.customBadge, + timestamp: this.customTimestamp + }; + } + + getRelativeTime(timestamp: number): string { + if (!timestamp) return ''; + const diff = Math.floor(Date.now() / 1000) - timestamp; + if (diff < 60) return 'Now'; + if (diff < 3600) return `${Math.floor(diff / 60)}m`; + if (diff < 86400) return `${Math.floor(diff / 3600)}h`; + return `${Math.floor(diff / 86400)}d`; + } +} +``` + +## New Feature: Granular Events + +The refactored component emits separate events for specific element clicks, allowing precise handling of user interactions. + +### Before: Generic Click Handling + +```typescript +// Before: Only had itemClick, had to determine what was clicked +@Component({ + template: ` + + ` +}) +export class BeforeEventsComponent { + onConversationClick(conversation: any): void { + // Couldn't distinguish between avatar click, title click, etc. + // Had to open the conversation regardless of what was clicked + this.openConversation(conversation); + } +} +``` + +### After: Granular Event Handling + +```typescript +// After: Separate events for each clickable element +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatConversationsComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + template: ` + + ` +}) +export class AfterEventsComponent { + onConversationClick(conversation: CometChat.Conversation): void { + // Open the conversation + this.openConversation(conversation); + } + + onAvatarClick(conversation: CometChat.Conversation): void { + // Show user/group profile instead of opening conversation + this.showProfile(conversation); + } + + onTitleClick(conversation: CometChat.Conversation): void { + // Could open rename dialog for groups + console.log('Title clicked'); + } + + onSubtitleClick(conversation: CometChat.Conversation): void { + // Jump to the last message + this.jumpToLastMessage(conversation); + } + + onTimestampClick(conversation: CometChat.Conversation): void { + // Show full date/time details + this.showMessageDetails(conversation); + } + + onBadgeClick(conversation: CometChat.Conversation): void { + // Mark conversation as read + this.markAsRead(conversation); + } + + onContextMenuOpen(conversation: CometChat.Conversation): void { + // Track analytics or prepare menu options + console.log('Context menu opened for:', conversation); + } + + onScrollToTop(): void { + // Could refresh the list + console.log('Scrolled to top'); + } + + onScrollToBottom(): void { + // Could show "back to top" button + console.log('Scrolled to bottom'); + } + + onSelectionChange(state: { selectedIds: Set; count: number }): void { + // Update toolbar with selection count + console.log('Selected:', state.count, 'conversations'); + } + + private openConversation(conversation: CometChat.Conversation): void { /* ... */ } + private showProfile(conversation: CometChat.Conversation): void { /* ... */ } + private jumpToLastMessage(conversation: CometChat.Conversation): void { /* ... */ } + private showMessageDetails(conversation: CometChat.Conversation): void { /* ... */ } + private markAsRead(conversation: CometChat.Conversation): void { /* ... */ } +} +``` expandable + + +### New Events Summary + +| Event | Payload | Description | +|-------|---------|-------------| +| `avatarClick` | `Conversation` | Avatar element clicked | +| `titleClick` | `Conversation` | Title element clicked | +| `subtitleClick` | `Conversation` | Subtitle element clicked | +| `timestampClick` | `Conversation` | Timestamp element clicked | +| `badgeClick` | `Conversation` | Unread badge clicked | +| `contextMenuOpen` | `Conversation` | Context menu opened | +| `scrollToTop` | `void` | List scrolled to top | +| `scrollToBottom` | `void` | List scrolled to bottom | +| `selectionChange` | `SelectionState` | Selection state changed | + + + Granular click events automatically call `stopPropagation()` to prevent the main `itemClick` event from firing. This allows you to handle specific element clicks without triggering the default conversation open behavior. + + +## New Feature: CometChatConversationItem + +The `CometChatConversationItem` component is now available as a standalone component. You can use it independently to render conversation items in custom layouts. + +### Standalone Usage + +```typescript +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatConversationItemComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-custom-layout', + standalone: true, + imports: [CometChatConversationItemComponent], + template: ` +
+ @for (conversation of conversations; track conversation.getConversationId()) { + + } +
+ `, + styles: [` + .custom-conversation-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: var(--cometchat-spacing-4); + } + `] +}) +export class CustomLayoutComponent { + conversations: CometChat.Conversation[] = []; + activeId: string | null = null; + selectedIds = new Set(); + + onItemClick(conversation: CometChat.Conversation): void { + this.activeId = conversation.getConversationId(); + } + + onAvatarClick(conversation: CometChat.Conversation): void { + // Show profile + } + + onBadgeClick(conversation: CometChat.Conversation): void { + // Mark as read + } +} +``` expandable + +### CometChatConversationItem Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `conversation` | `Conversation` | **Required** | The conversation to render | +| `isActive` | `boolean` | `false` | Whether this is the active conversation | +| `isSelected` | `boolean` | `false` | Whether this is selected (selection mode) | +| `isFocused` | `boolean` | `false` | Whether this has keyboard focus | +| `typingIndicator` | `TypingIndicator \| null` | `null` | Typing indicator if someone is typing | +| `hideReceipts` | `boolean` | `false` | Hide message receipts | +| `hideUserStatus` | `boolean` | `false` | Hide online/offline status | +| `hideGroupType` | `boolean` | `false` | Hide group type icon | +| `dateFormat` | `CalendarObject` | `undefined` | Custom date format | +| `slots` | `Partial` | `undefined` | Slot customization | +| `contextMenuOptions` | `CometChatOption[]` | `undefined` | Context menu options | + +## New Feature: CometChatPaginatedList + +The `CometChatPaginatedList` is a generic component for infinite scroll lists. It's used internally by CometChatConversations but can also be used independently. + +### Standalone Usage + +```typescript +import { Component, ViewChild } from '@angular/core'; +import { CometChatPaginatedListComponent } from '@cometchat/chat-uikit-angular'; + +interface CustomItem { + id: string; + name: string; + description: string; +} + +@Component({ + selector: 'app-custom-list', + standalone: true, + imports: [CometChatPaginatedListComponent], + template: ` + + +
+ #{{ index + 1 }} + {{ item.name }} + {{ item.description }} +
+
+ + +
+

No items found

+
+
+ + +
+

{{ error.message }}

+ +
+
+
+ ` +}) +export class CustomListComponent { + @ViewChild('list') list!: CometChatPaginatedListComponent; + + items: CustomItem[] = []; + isLoading = false; + hasMore = true; + error: Error | null = null; + + trackById = (index: number, item: CustomItem) => item.id; + + async onLoadMore(): Promise { + try { + const newItems = await this.fetchItems(); + this.items = [...this.items, ...newItems]; + this.hasMore = newItems.length > 0; + } catch (err) { + this.error = err as Error; + } finally { + // IMPORTANT: Always call loadComplete() + this.list.loadComplete(); + } + } + + onItemClick(event: { item: CustomItem; index: number }): void { + console.log('Clicked:', event.item.name); + } + + retry(): void { + this.error = null; + this.onLoadMore(); + } + + private async fetchItems(): Promise { + // Your API call + return []; + } +} +``` + + +## New Feature: CSS Variables for Theming + +The refactored component introduces comprehensive CSS variables for theming without code changes. All variables follow the naming convention `--cometchat-conversations-{element}-{property}`. + +### Before: Custom CSS Overrides + +```css +/* Before: Had to use deep selectors and !important */ +::ng-deep .cometchat-conversations .conversation-item { + background-color: #f5f5f5 !important; + padding: 16px !important; +} + +::ng-deep .cometchat-conversations .conversation-item:hover { + background-color: #e0e0e0 !important; +} + +::ng-deep .cometchat-conversations .unread-badge { + background-color: #FF6B35 !important; +} +``` + +### After: CSS Variables + +```css +/* After: Clean CSS variable overrides */ +cometchat-conversations { + /* Container */ + --cometchat-conversations-background: #ffffff; + --cometchat-conversations-padding: 0; + + /* List items */ + --cometchat-conversations-item-background: #f5f5f5; + --cometchat-conversations-item-background-hover: #e0e0e0; + --cometchat-conversations-item-background-active: #d0d0ff; + --cometchat-conversations-item-padding: var(--cometchat-spacing-4); + --cometchat-conversations-item-gap: var(--cometchat-spacing-3); + + /* Avatar */ + --cometchat-conversations-avatar-size: 48px; + --cometchat-conversations-avatar-border-radius: 50%; + + /* Status indicator */ + --cometchat-conversations-status-size: 14px; + --cometchat-conversations-status-online-color: #4CAF50; + --cometchat-conversations-status-offline-color: #999999; + + /* Title */ + --cometchat-conversations-title-text-font: var(--cometchat-font-body-medium); + --cometchat-conversations-title-text-color: var(--cometchat-text-color-primary); + + /* Subtitle */ + --cometchat-conversations-subtitle-font: var(--cometchat-font-caption1-regular); + --cometchat-conversations-subtitle-color: var(--cometchat-text-color-secondary); + + /* Timestamp */ + --cometchat-conversations-timestamp-font: var(--cometchat-font-caption2-regular); + --cometchat-conversations-timestamp-color: var(--cometchat-text-color-tertiary); + + /* Unread badge */ + --cometchat-conversations-badge-background: #FF6B35; + --cometchat-conversations-badge-color: #ffffff; + --cometchat-conversations-badge-font-size: 10px; + --cometchat-conversations-badge-min-width: 18px; + --cometchat-conversations-badge-height: 18px; + --cometchat-conversations-badge-border-radius: var(--cometchat-radius-max); + + /* Receipts */ + --cometchat-conversations-receipt-size: 16px; + --cometchat-conversations-receipt-sent-color: var(--cometchat-neutral-color-500); + --cometchat-conversations-receipt-delivered-color: var(--cometchat-neutral-color-600); + --cometchat-conversations-receipt-read-color: #56E8A7; +} +``` + +### Dark Theme Example + +```css +/* Dark theme using CSS variables */ +.dark-theme cometchat-conversations { + --cometchat-conversations-background: #1a1a1a; + --cometchat-conversations-item-background: #1a1a1a; + --cometchat-conversations-item-background-hover: #2a2a2a; + --cometchat-conversations-item-background-active: #3a3a4a; + --cometchat-conversations-item-border-bottom: 1px solid #333333; + --cometchat-conversations-title-text-color: #ffffff; + --cometchat-conversations-subtitle-color: #cccccc; + --cometchat-conversations-timestamp-color: #999999; +} +``` + +### Brand Colors Example + +```css +/* Custom brand colors */ +.branded cometchat-conversations { + --cometchat-conversations-badge-background: #FF6B35; + --cometchat-conversations-status-online-color: #00D084; + --cometchat-conversations-item-background-active: #fff5f2; + --cometchat-conversations-item-focus-box-shadow: inset 0 0 0 2px #FF6B35; + --cometchat-conversations-receipt-read-color: #00D084; +} +``` expandable + +### CSS Variables Reference + +#### Container Variables (6) + +| Variable | Default | Description | +|----------|---------|-------------| +| `--cometchat-conversations-background` | `var(--cometchat-background-color-01)` | Container background | +| `--cometchat-conversations-padding` | `0` | Container padding | +| `--cometchat-conversations-border` | `none` | Container border | +| `--cometchat-conversations-border-radius` | `0` | Container border radius | +| `--cometchat-conversations-min-height` | `100%` | Minimum height | +| `--cometchat-conversations-max-height` | `100%` | Maximum height | + +#### Header Variables (5) + +| Variable | Default | Description | +|----------|---------|-------------| +| `--cometchat-conversations-header-background` | `transparent` | Header background | +| `--cometchat-conversations-header-padding` | `var(--cometchat-spacing-4)` | Header padding | +| `--cometchat-conversations-header-border-bottom` | `1px solid var(--cometchat-border-color-light)` | Header border | +| `--cometchat-conversations-title-font` | `var(--cometchat-font-heading1-bold)` | Title font | +| `--cometchat-conversations-title-color` | `var(--cometchat-text-color-primary)` | Title color | + +#### List Item Variables (12) + +| Variable | Default | Description | +|----------|---------|-------------| +| `--cometchat-conversations-item-background` | `var(--cometchat-background-color-01)` | Item background | +| `--cometchat-conversations-item-background-hover` | `var(--cometchat-background-color-02)` | Hover background | +| `--cometchat-conversations-item-background-active` | `var(--cometchat-background-color-03)` | Active background | +| `--cometchat-conversations-item-background-selected` | `var(--cometchat-background-color-03)` | Selected background | +| `--cometchat-conversations-item-background-unread` | `var(--cometchat-background-color-01)` | Unread background | +| `--cometchat-conversations-item-padding` | `var(--cometchat-spacing-3) var(--cometchat-spacing-4)` | Item padding | +| `--cometchat-conversations-item-gap` | `var(--cometchat-spacing-3)` | Gap between elements | +| `--cometchat-conversations-item-border-bottom` | `1px solid var(--cometchat-border-color-light)` | Item border | +| `--cometchat-conversations-item-border-radius` | `0` | Item border radius | +| `--cometchat-conversations-item-transition` | `background-color 0.2s ease` | Transition effect | +| `--cometchat-conversations-item-focus-outline` | `none` | Focus outline | +| `--cometchat-conversations-item-focus-box-shadow` | `inset 0 0 0 2px var(--cometchat-primary-color)` | Focus indicator | + + +#### Avatar Variables (4) + +| Variable | Default | Description | +|----------|---------|-------------| +| `--cometchat-conversations-avatar-size` | `48px` | Avatar size | +| `--cometchat-conversations-avatar-border-radius` | `50%` | Avatar border radius | +| `--cometchat-conversations-avatar-border` | `none` | Avatar border | +| `--cometchat-conversations-avatar-font-size` | `20px` | Avatar initials font size | + +#### Status Indicator Variables (6) + +| Variable | Default | Description | +|----------|---------|-------------| +| `--cometchat-conversations-status-size` | `14px` | Status indicator size | +| `--cometchat-conversations-status-border` | `2px solid var(--cometchat-background-color-01)` | Status border | +| `--cometchat-conversations-status-online-color` | `var(--cometchat-success-color)` | Online color | +| `--cometchat-conversations-status-offline-color` | `var(--cometchat-neutral-color-400)` | Offline color | +| `--cometchat-conversations-status-position-bottom` | `0` | Position from bottom | +| `--cometchat-conversations-status-position-right` | `0` | Position from right | + +#### Title & Subtitle Variables (10) + +| Variable | Default | Description | +|----------|---------|-------------| +| `--cometchat-conversations-title-text-font` | `var(--cometchat-font-body-medium)` | Title font | +| `--cometchat-conversations-title-text-color` | `var(--cometchat-text-color-primary)` | Title color | +| `--cometchat-conversations-title-text-color-unread` | `var(--cometchat-text-color-primary)` | Title color (unread) | +| `--cometchat-conversations-title-text-font-weight-unread` | `700` | Title weight (unread) | +| `--cometchat-conversations-title-max-width` | `100%` | Title max width | +| `--cometchat-conversations-subtitle-font` | `var(--cometchat-font-caption1-regular)` | Subtitle font | +| `--cometchat-conversations-subtitle-color` | `var(--cometchat-text-color-secondary)` | Subtitle color | +| `--cometchat-conversations-subtitle-color-unread` | `var(--cometchat-text-color-primary)` | Subtitle color (unread) | +| `--cometchat-conversations-subtitle-font-weight-unread` | `600` | Subtitle weight (unread) | +| `--cometchat-conversations-subtitle-max-width` | `100%` | Subtitle max width | + +#### Badge & Receipt Variables (12) + +| Variable | Default | Description | +|----------|---------|-------------| +| `--cometchat-conversations-badge-background` | `var(--cometchat-primary-color)` | Badge background | +| `--cometchat-conversations-badge-color` | `#ffffff` | Badge text color | +| `--cometchat-conversations-badge-font-size` | `10px` | Badge font size | +| `--cometchat-conversations-badge-font-weight` | `600` | Badge font weight | +| `--cometchat-conversations-badge-min-width` | `18px` | Badge minimum width | +| `--cometchat-conversations-badge-height` | `18px` | Badge height | +| `--cometchat-conversations-badge-padding` | `0 5px` | Badge padding | +| `--cometchat-conversations-badge-border-radius` | `var(--cometchat-radius-max)` | Badge border radius | +| `--cometchat-conversations-receipt-size` | `16px` | Receipt icon size | +| `--cometchat-conversations-receipt-sent-color` | `var(--cometchat-neutral-color-500)` | Sent receipt color | +| `--cometchat-conversations-receipt-delivered-color` | `var(--cometchat-neutral-color-600)` | Delivered receipt color | +| `--cometchat-conversations-receipt-read-color` | `#56E8A7` | Read receipt color | + +#### Empty & Error State Variables (16) + +| Variable | Default | Description | +|----------|---------|-------------| +| `--cometchat-conversations-empty-padding` | `var(--cometchat-spacing-8) var(--cometchat-spacing-4)` | Empty state padding | +| `--cometchat-conversations-empty-min-height` | `300px` | Empty state min height | +| `--cometchat-conversations-empty-icon-size` | `80px` | Empty icon size | +| `--cometchat-conversations-empty-icon-color` | `var(--cometchat-icon-color-tertiary)` | Empty icon color | +| `--cometchat-conversations-empty-title-font` | `var(--cometchat-font-heading3-medium)` | Empty title font | +| `--cometchat-conversations-empty-title-color` | `var(--cometchat-text-color-primary)` | Empty title color | +| `--cometchat-conversations-empty-message-font` | `var(--cometchat-font-body-regular)` | Empty message font | +| `--cometchat-conversations-empty-message-color` | `var(--cometchat-text-color-secondary)` | Empty message color | +| `--cometchat-conversations-error-padding` | `var(--cometchat-spacing-8) var(--cometchat-spacing-4)` | Error state padding | +| `--cometchat-conversations-error-min-height` | `200px` | Error state min height | +| `--cometchat-conversations-error-icon-size` | `64px` | Error icon size | +| `--cometchat-conversations-error-icon-color` | `var(--cometchat-error-color)` | Error icon color | +| `--cometchat-conversations-error-title-font` | `var(--cometchat-font-heading3-medium)` | Error title font | +| `--cometchat-conversations-error-title-color` | `var(--cometchat-text-color-primary)` | Error title color | +| `--cometchat-conversations-error-button-background` | `var(--cometchat-primary-button-background)` | Retry button background | +| `--cometchat-conversations-error-button-color` | `var(--cometchat-primary-button-text)` | Retry button text color | + +## Progressive Enhancement Pattern + +The refactoring supports a progressive enhancement approach - start with basic usage and add customization as needed: + +### Level 1: Basic Usage (No Changes) + +```typescript +// Start simple - works exactly as before + +``` + +### Level 2: Add CSS Variable Theming + +```css +/* Add theming without touching TypeScript */ +cometchat-conversations { + --cometchat-conversations-badge-background: #FF6B35; + --cometchat-conversations-item-background-active: #fff5f2; +} +``` + +### Level 3: Add Granular Events + +```typescript +// Add specific event handlers as needed + +``` + +### Level 4: Add Slot Customization + +```typescript +// Customize specific elements + + + {{ count }} + + +``` + +### Level 5: SDK-Wide Templates + +```typescript +// Register templates that apply everywhere +ngAfterViewInit(): void { + this.templatesService.setConversationSubtitleTemplate(this.globalSubtitle); +} +``` + +## Summary + +| Feature | Backward Compatible | Migration Required | +|---------|--------------------|--------------------| +| Existing @Input properties | ✅ Yes | None | +| Existing @Output events | ✅ Yes | None | +| Existing template customization | ✅ Yes | None | +| CometChatTemplatesService | ✅ Yes (additive) | Optional adoption | +| Slot-based customization | ✅ Yes (additive) | Optional adoption | +| Granular events | ✅ Yes (additive) | Optional adoption | +| CSS variables | ✅ Yes (additive) | Optional adoption | +| CometChatConversationItem | ✅ Yes (new component) | Optional adoption | +| CometChatPaginatedList | ✅ Yes (new component) | Optional adoption | + + + The enterprise refactoring is designed for incremental adoption. Start with your existing code, then progressively enhance with new features as needed. There's no need to migrate everything at once. + + +## Related Documentation + +- [CometChatConversations](/ui-kit/angular/v5/components/cometchat-conversations) - Main component documentation +- [CometChatConversationItem](/ui-kit/angular/v5/components/cometchat-conversation-item) - Standalone item component +- [CometChatPaginatedList](/ui-kit/angular/v5/components/cometchat-paginated-list) - Generic paginated list component +- [Theming](/ui-kit/angular/v5/customization/theming) - General theming guide +- [Localization](/ui-kit/angular/v5/customization/localization) - Localization guide diff --git a/ui-kit/angular/v5/guides/custom-message-types.mdx b/ui-kit/angular/v5/guides/custom-message-types.mdx new file mode 100644 index 000000000..b0bcfca4e --- /dev/null +++ b/ui-kit/angular/v5/guides/custom-message-types.mdx @@ -0,0 +1,370 @@ +--- +title: "Custom Message Types" +description: "Register custom message bubble types, override conversation subtitles, and include custom messages in fetching." +--- + + + +| Field | Value | +| --- | --- | +| Package | `@cometchat/chat-uikit-angular` | +| Key services | `MessageBubbleConfigService`, `ConversationSubtitleService`, `MessageListService` | +| Required setup | `CometChatUIKit.init(uiKitSettings)` then `CometChatUIKit.login("UID")` | +| Purpose | Add custom message types with custom bubble templates, subtitle overrides, and fetch inclusion | +| Features | Custom bubble rendering, subtitle formatting, icon overrides, message type fetching | +| Related | [Message Bubble](/ui-kit/angular/v5/components/cometchat-message-bubble) \| [Conversations](/ui-kit/angular/v5/components/cometchat-conversations) \| [Message List](/ui-kit/angular/v5/components/cometchat-message-list) | + + + +The CometChat Angular UIKit supports extending the message system with custom message types. You can register custom bubble templates, override how messages appear in the conversation list, and include custom types in message fetching — all without modifying UIKit source code. + +| Capability | Description | +| --- | --- | +| Custom bubble templates | Register `ng-template` views for new message types | +| Conversation subtitle override | Control last message text per message type | +| Icon overrides | Set custom icons for conversation list items | +| Fetch inclusion | Include custom types in the message request builder | + +--- + +## Custom Message Bubble + +Use `MessageBubbleConfigService` to register templates for custom message types. The message type key format is `{type}_{category}` (e.g., `location_custom`). + +### Register a Custom Content View + +```typescript expandable +import { Component, ViewChild, TemplateRef, AfterViewInit, inject } from '@angular/core'; +import { MessageBubbleConfigService } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-chat', + template: ` + +
+ Location + {{ getLocationLabel(message) }} +
+
+ + + ` +}) +export class ChatComponent implements AfterViewInit { + @ViewChild('locationBubble') locationBubble!: TemplateRef; + + private bubbleConfig = inject(MessageBubbleConfigService); + + ngAfterViewInit() { + this.bubbleConfig.setBubbleView('location_custom', { + contentView: this.locationBubble + }); + } + + getMapUrl(message: any): string { + const data = message.getCustomData(); + return `https://maps.example.com/static?lat=${data.latitude}&lng=${data.longitude}`; + } + + getLocationLabel(message: any): string { + return message.getCustomData()?.label || 'Shared location'; + } +} +``` + +The template receives the message object as `$implicit` context, so `let-message` gives you full access to the `CometChat.BaseMessage`. + +### Full Bubble Override + +To replace the entire bubble structure (header, content, footer, status), register a `bubbleView`: + +```typescript +this.bubbleConfig.setBubbleView('location_custom', { + bubbleView: this.fullLocationBubble +}); +``` + +The `bubbleView` template context includes `$implicit` (message), `alignment`, and `group`. + +### Partial Override + +Register only the parts you want to customize. Unregistered parts use defaults: + +```typescript +this.bubbleConfig.setBubbleView('location_custom', { + contentView: this.locationContent, + footerView: this.locationFooter + // headerView, statusInfoView, etc. use defaults +}); +``` + +--- + +## Conversation Subtitle Override + +Use `ConversationSubtitleService` to control the last message text shown in the conversation list for specific message types. + +### Register a Subtitle Formatter + +```typescript expandable +import { inject } from '@angular/core'; +import { ConversationSubtitleService } from '@cometchat/chat-uikit-angular'; + +export class AppComponent { + private subtitleService = inject(ConversationSubtitleService); + + ngOnInit() { + // Override subtitle for location messages + this.subtitleService.registerSubtitleFormatter('location_custom', (message) => { + return '📍 Shared a location'; + }); + + // Override subtitle for payment messages + this.subtitleService.registerSubtitleFormatter('payment_custom', (message) => { + const data = (message as any).getCustomData(); + return `💰 Payment: $${data.amount}`; + }); + } +} +``` + +### Override Subtitle Icon + +```typescript +this.subtitleService.registerSubtitleIconOverride('location_custom', 'location-pin'); +``` + +### Unregister + +```typescript +this.subtitleService.unregisterSubtitleFormatter('location_custom'); +``` + +--- + +## Custom Message Fetching + +By default, the message list fetches these types and categories: + +**Default Types:** `text`, `file`, `image`, `audio`, `video`, `groupMember`, `form`, `scheduler`, `card`, `assistant`, `extension_sticker`, `extension_poll`, `extension_whiteboard`, `extension_document`, `meeting` + +**Default Categories:** `message`, `custom`, `call`, `interactive`, `agentic`, `action` (action is excluded when `hideGroupActionMessages` is true) + +### Inspect Current Defaults + +```typescript expandable +import { inject } from '@angular/core'; +import { MessageListService } from '@cometchat/chat-uikit-angular'; + +export class AppComponent { + private messageListService = inject(MessageListService); + + ngOnInit() { + console.log(this.messageListService.getAllMessageTypes()); + console.log(this.messageListService.getAllMessageCategories()); + } +} +``` + +### Add Custom Types and Categories + +Append to the existing defaults: + +```typescript +// Add custom message types alongside defaults +this.messageListService.addCustomMessageTypes(['location', 'payment']); + +// Add custom categories alongside defaults +this.messageListService.addCustomMessageCategories(['custom']); +``` + +### Replace Types and Categories Entirely + +Fully replace the defaults with your own list: + +```typescript +// Only fetch text and image messages +this.messageListService.setMessageTypes(['text', 'image', 'location']); + +// Only fetch message and custom categories +this.messageListService.setMessageCategories(['message', 'custom']); +``` + +Pass `null` to revert to the built-in defaults: + +```typescript +this.messageListService.setMessageTypes(null); +this.messageListService.setMessageCategories(null); +``` + +### Remove Custom Types + +```typescript +this.messageListService.removeCustomMessageTypes(['location']); +this.messageListService.removeCustomMessageCategories(['custom']); +``` + + +If you provide a custom `MessagesRequestBuilder` via `setMessagesRequestBuilder`, custom types/categories registered via `addCustomMessageTypes`/`addCustomMessageCategories` are not appended. The custom builder is used as-is. + + +--- + +## End-to-End Example: Location Sharing + +A complete example showing a custom "location" message type with bubble template, subtitle override, and fetch inclusion. + + + +```typescript expandable +import { + Component, ViewChild, TemplateRef, AfterViewInit, inject, OnInit +} from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + MessageBubbleConfigService, + ConversationSubtitleService, + MessageListService, + CometChatMessageListComponent, + CometChatConversationsComponent +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-location-demo', + standalone: true, + imports: [CometChatMessageListComponent, CometChatConversationsComponent], + template: ` + +
+
📍 Location
+
{{ getAddress(message) }}
+ Open in Maps +
+
+ + + + ` +}) +export class LocationDemoComponent implements OnInit, AfterViewInit { + @ViewChild('locationBubble') locationBubble!: TemplateRef; + + user: CometChat.User | undefined; + + private bubbleConfig = inject(MessageBubbleConfigService); + private subtitleService = inject(ConversationSubtitleService); + private messageListService = inject(MessageListService); + + ngOnInit() { + // 1. Include 'location' type in message fetching + this.messageListService.addCustomMessageTypes(['location']); + + // 2. Override conversation subtitle + this.subtitleService.registerSubtitleFormatter('location_custom', (msg) => { + const data = (msg as CometChat.CustomMessage).getCustomData() as any; + return `📍 ${data?.address || 'Shared a location'}`; + }); + + // Load user + CometChat.getUser('uid').then(u => this.user = u); + } + + ngAfterViewInit() { + // 3. Register custom bubble template + this.bubbleConfig.setBubbleView('location_custom', { + contentView: this.locationBubble + }); + } + + getAddress(message: any): string { + return message.getCustomData()?.address || 'Unknown location'; + } + + getMapsLink(message: any): string { + const data = message.getCustomData(); + return `https://maps.google.com/?q=${data?.latitude},${data?.longitude}`; + } +} +``` +
+ + +```typescript expandable +// Send a custom location message +const receiverID = 'uid'; +const customData = { + latitude: 37.7749, + longitude: -122.4194, + address: '123 Main St, San Francisco, CA' +}; + +const customMessage = new CometChat.CustomMessage( + receiverID, + CometChat.RECEIVER_TYPE.USER, + 'location', + customData +); + +CometChat.sendCustomMessage(customMessage).then( + (message) => console.log('Location sent:', message), + (error) => console.error('Error:', error) +); +``` + +
+ +--- + +## API Reference + +### MessageBubbleConfigService + +| Method | Description | +| --- | --- | +| `setBubbleView(typeKey, partMap)` | Register templates for a message type | +| `setMessageTemplates(maps)` | Register templates for multiple types at once | +| `setGlobalView(part, template)` | Set a global template for a bubble part | +| `getView(typeKey, part)` | Get the configured template for a type and part | + +### ConversationSubtitleService + +| Method | Description | +| --- | --- | +| `registerSubtitleFormatter(typeKey, formatter)` | Register a subtitle formatter callback | +| `unregisterSubtitleFormatter(typeKey)` | Remove a subtitle formatter | +| `registerSubtitleIconOverride(typeKey, iconName)` | Override the subtitle icon | +| `getSubtitle(typeKey, message)` | Get formatted subtitle (or null) | +| `getIconOverride(typeKey)` | Get icon override (or null) | +| `hasFormatter(typeKey)` | Check if a formatter is registered | + +### MessageListService + +| Method | Description | +| --- | --- | +| `getAllMessageTypes()` | Get the current effective list of message types | +| `getAllMessageCategories()` | Get the current effective list of message categories | +| `addCustomMessageTypes(types)` | Append custom types to the defaults | +| `addCustomMessageCategories(categories)` | Append custom categories to the defaults | +| `setMessageTypes(types)` | Replace the entire types array (pass `null` to reset) | +| `setMessageCategories(categories)` | Replace the entire categories array (pass `null` to reset) | +| `removeCustomMessageTypes(types)` | Remove previously added custom types | +| `removeCustomMessageCategories(categories)` | Remove previously added custom categories | + +--- + +## Next Steps + + + + Customize the message bubble component. + + + Customize the conversations list. + + + Override text and translations. + + + Customize date and time display. + + diff --git a/ui-kit/angular/v5/guides/rich-text-formatting.mdx b/ui-kit/angular/v5/guides/rich-text-formatting.mdx new file mode 100644 index 000000000..3d61c75b2 --- /dev/null +++ b/ui-kit/angular/v5/guides/rich-text-formatting.mdx @@ -0,0 +1,642 @@ +# Rich Text Formatting Guide + +This guide explains how to use rich text formatting in the CometChat Angular V5 UIKit, including text formatting, mentions, links, and more. + +## Overview + +The UIKit provides a custom rich text editor built on native browser APIs, offering: +- Lightweight implementation (no external dependencies) +- Full formatting support (bold, italic, underline, etc.) +- Mentions integration +- Undo/redo history +- Keyboard shortcuts +- Full accessibility + +## Basic Usage + +### In MessageComposer + +The `CometChatMessageComposer` component includes rich text formatting by default: + +```html + + +``` + +### Custom Implementation + +To use the rich text editor in your own components: + +```typescript expandable +import { Component, OnInit, OnDestroy, inject } from '@angular/core'; +import { RichTextEditorService, RichTextEditor } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-custom-editor', + template: ` +
+
+
+ + + +
+
+ ` +}) +export class CustomEditorComponent implements OnInit, OnDestroy { + private editorService = inject(RichTextEditorService); + private editor!: RichTextEditor; + + @ViewChild('editorElement') editorElement!: ElementRef; + + ngOnInit() { + this.editor = this.editorService.createEditor({ + placeholder: 'Type something...', + autofocus: true, + onUpdate: (html, text) => { + console.log('Content:', html, text); + } + }, this.editorElement.nativeElement); + } + + ngOnDestroy() { + this.editorService.destroyEditor(this.editor); + } + + toggleBold() { + this.editorService.toggleBold(this.editor); + } + + toggleItalic() { + this.editorService.toggleItalic(this.editor); + } + + toggleUnderline() { + this.editorService.toggleUnderline(this.editor); + } +} +``` + +## Text Formatting + +### Inline Formatting + +Apply formatting to selected text or at cursor position: + +```typescript expandable +// Bold +this.editorService.toggleBold(this.editor); + +// Italic +this.editorService.toggleItalic(this.editor); + +// Underline +this.editorService.toggleUnderline(this.editor); + +// Strikethrough +this.editorService.toggleStrikethrough(this.editor); + +// Inline code +this.editorService.toggleCode(this.editor); +``` + +### Keyboard Shortcuts + +Users can apply formatting using keyboard shortcuts: + +| Format | Windows/Linux | Mac | +|--------|--------------|-----| +| Bold | `Ctrl+B` | `Cmd+B` | +| Italic | `Ctrl+I` | `Cmd+I` | +| Underline | `Ctrl+U` | `Cmd+U` | +| Undo | `Ctrl+Z` | `Cmd+Z` | +| Redo | `Ctrl+Y` or `Ctrl+Shift+Z` | `Cmd+Shift+Z` | + +### Block Formatting + +Apply formatting to entire blocks: + +```typescript +// Code block +this.editorService.toggleCodeBlock(this.editor); + +// Blockquote +this.editorService.toggleBlockquote(this.editor); +``` + +## Lists + +### Creating Lists + +```typescript +// Ordered (numbered) list +this.editorService.toggleOrderedList(this.editor); + +// Unordered (bullet) list +this.editorService.toggleBulletList(this.editor); +``` + +### List Behavior + +- **Enter**: Creates a new list item +- **Enter** (twice): Exits the list +- **Tab**: Indents the list item +- **Shift+Tab**: Outdents the list item + +### Example + +```typescript +// Create a numbered list +this.editorService.toggleOrderedList(this.editor); + +// User types: +// 1. First item [Enter] +// 2. Second item [Enter] +// 3. Third item [Enter][Enter] +// (exits list) +``` + +## Links + +### Adding Links + +```typescript +addLink() { + const url = prompt('Enter URL:'); + if (url) { + this.editorService.setLink(this.editor, url); + } +} +``` + +### Removing Links + +```typescript +removeLink() { + this.editorService.setLink(this.editor, null); +} +``` + +### URL Validation + +The editor automatically validates URLs: +- Invalid URLs display an error +- URLs without protocol are prefixed with `https://` +- Pasted URLs are automatically converted to clickable links + +### Example + +```typescript +// Valid URLs +this.editorService.setLink(this.editor, 'https://example.com'); +this.editorService.setLink(this.editor, 'example.com'); // Auto-prefixed + +// Invalid URL +this.editorService.setLink(this.editor, 'not a url'); // Shows error +``` + +## Mentions + +### Basic Mention Integration + +The editor integrates with `CometChatMentionsFormatter` for @mention functionality: + +```typescript expandable +ngOnInit() { + this.editor = this.editorService.createEditor({ + placeholder: 'Type @ to mention someone...', + onMentionStart: (query) => { + this.showMentionPopup(query); + }, + onMentionEnd: () => { + this.hideMentionPopup(); + }, + getMentionSuggestions: async (query) => { + return await this.searchUsers(query); + }, + onMentionSelect: (item) => { + this.insertMention(item); + } + }); +} +``` + +### Inserting Mentions + +```typescript expandable +insertMention(user: CometChat.User) { + const isSelf = user.getUid() === this.loggedInUser.getUid(); + + this.editorService.insertMention( + this.editor, + user.getUid(), + user.getName(), + this.queryLength + 1, // +1 for @ symbol + isSelf + ); +} +``` + +### Mention Styling + +Mentions are styled differently based on whether they're for the current user: + +```css expandable +/* Self mention (logged-in user) */ +.cometchat-mention--self { + background-color: var(--cometchat-primary-color); + color: var(--cometchat-static-white); +} + +/* Other user mention */ +.cometchat-mention--other { + background-color: var(--cometchat-background-color-03); + color: var(--cometchat-text-color-primary); +} +``` + +### Mention Limits + +- Maximum of **10 unique mentions** per message +- Attempting to add more displays an error + +### Getting Mention Data + +```typescript +// Get text with CometChat mention format +const formattedText = this.editorService.getTextWithMentionFormat(this.editor); +// Output: "Hello <@uid:user123>, how are you?" + +// Get unique mention UIDs +const mentionUids = this.editorService.getUniqueMentionUids(this.editor); +console.log('Mentioned users:', Array.from(mentionUids)); +``` + +### Loading Messages with Mentions + +When loading a message that contains mentions: + +```typescript +loadMessage(message: CometChat.TextMessage) { + this.editorService.setContentWithMentions( + this.editor, + message.getText(), + message.getMentionedUsers() + ); +} +``` + +## Undo/Redo + +### Using History + +```typescript +// Undo +if (this.editorService.canUndo(this.editor)) { + this.editorService.undo(this.editor); +} + +// Redo +if (this.editorService.canRedo(this.editor)) { + this.editorService.redo(this.editor); +} +``` + +### History Grouping + +Rapid typing is automatically grouped into single undo steps: +- Typing delay: 500ms +- Each formatting operation creates a new undo step +- History stack size: 100 entries + +### Example + +```typescript +// User types "Hello world" quickly +// This creates ONE undo step + +// User applies bold +// This creates a SECOND undo step + +// Pressing Ctrl+Z undoes the bold +// Pressing Ctrl+Z again removes "Hello world" +``` + +## Content Management + +### Getting Content + +```typescript +// Get HTML with formatting +const html = this.editorService.getHTML(this.editor); + +// Get plain text without formatting +const text = this.editorService.getText(this.editor); + +// Get metadata +const metadata = this.editorService.getRichTextMetadata(this.editor); +console.log('Has formatting:', metadata.hasFormatting); +``` + +### Setting Content + +```typescript +// Set HTML content +this.editorService.setContent(this.editor, '

Bold text

'); + +// Clear content +this.editorService.clearContent(this.editor); + +// Insert text at cursor +this.editorService.insertText(this.editor, 'Hello'); +``` + +### Checking Content State + +```typescript +// Check if empty +if (this.editorService.isEmpty(this.editor)) { + console.log('Editor is empty'); +} + +// Check if has formatting +if (this.editorService.hasFormatting(this.editor)) { + console.log('Content has rich text formatting'); +} +``` + +## Format State + +### Tracking Format State + +Use the reactive signal to track which formats are active: + +```typescript +formatState = this.editorService.formatState; + +// In template + + + +``` + +### Manual Format State Check + +```typescript +const formatState = this.editorService.getFormatState(this.editor); + +if (formatState.bold) { + console.log('Bold is active'); +} + +if (formatState.orderedList) { + console.log('In ordered list'); +} +``` + +## Copy, Paste, and Drag + +### Paste Handling + +The editor automatically handles pasted content: + +```typescript expandable +// Formatted text paste +// - Preserves compatible formatting (bold, italic, etc.) +// - Strips unsupported formatting +// - Sanitizes for XSS protection + +// Plain text paste +// - Inserts without formatting + +// URL paste +// - Automatically converts to clickable link +``` + +### Drag and Drop + +Users can drag and drop text within the editor: +- Formatting is preserved +- Cursor position is updated + +### Copy + +When users copy formatted text: +- Formatting is preserved in clipboard +- Both HTML and plain text formats are available + +## Accessibility + +### Keyboard Navigation + +All features are accessible via keyboard: +- **Tab**: Focus editor +- **Arrow keys**: Navigate content +- **Escape**: Close mention popup +- **Enter**: New line or list item +- **Formatting shortcuts**: See table above + +### Screen Reader Support + +The editor announces: +- Formatting changes ("Bold applied") +- Mention insertions ("Mentioned John Doe") +- Undo/redo operations ("Undone", "Redone") + +### ARIA Attributes + +The editor includes proper ARIA attributes: +- `role="textbox"` +- `aria-label="Message editor"` +- `aria-multiline="true"` +- `aria-placeholder="Type your message..."` + +## Styling + +### Using CSS Variables + +Customize the editor appearance using CSS variables: + +```css expandable +:root { + /* Editor background */ + --cometchat-background-color-01: #ffffff; + + /* Text color */ + --cometchat-text-color-primary: #141414; + + /* Placeholder color */ + --cometchat-text-color-secondary: #666666; + + /* Focus border */ + --cometchat-primary-color: #6852D6; + + /* Mention colors */ + --cometchat-primary-color: #6852D6; + --cometchat-background-color-03: #f0f0f0; +} +``` + +### Custom Styles + +Apply custom styles to the editor: + +```css expandable +.cometchat-rich-text-editor { + min-height: 100px; + max-height: 300px; + overflow-y: auto; + padding: var(--cometchat-spacing-3); + border: 1px solid var(--cometchat-border-color-light); + border-radius: var(--cometchat-radius-2); +} + +.cometchat-rich-text-editor:focus { + outline: none; + border-color: var(--cometchat-primary-color); +} +``` + +## Performance + +### Optimization Tips + +1. **Debounce updates**: Use debouncing for expensive operations +2. **Lazy load mentions**: Load mention suggestions on demand +3. **Limit history**: History is automatically limited to 100 entries +4. **Clean up**: Always destroy editors when done + +### Performance Metrics + +The editor is optimized for: +- **Typing latency**: < 50ms per keystroke (10,000 characters) +- **Formatting operations**: < 100ms +- **Initialization**: < 50ms +- **Paste operations**: < 200ms + +## Security + +### XSS Protection + +All HTML is sanitized to prevent XSS attacks: +- Script tags removed +- Event handlers removed +- Dangerous attributes removed +- Only safe HTML tags allowed + +### Content Validation + +- URLs are validated before insertion +- Mention data is sanitized +- Pasted content is sanitized + +## Best Practices + +### 1. Always Clean Up + +```typescript +ngOnDestroy() { + if (this.editor) { + this.editorService.destroyEditor(this.editor); + } +} +``` + +### 2. Use Reactive Signals + +```typescript +// Good: Use reactive signal +formatState = this.editorService.formatState; + +// Avoid: Polling format state +setInterval(() => { + this.formatState = this.editorService.getFormatState(this.editor); +}, 100); +``` + +### 3. Handle Empty State + +```typescript +sendMessage() { + if (this.editorService.isEmpty(this.editor)) { + // Show error: "Message cannot be empty" + return; + } + + const text = this.editorService.getTextWithMentionFormat(this.editor); + // Send message... +} +``` + +### 4. Validate Before Sending + +```typescript expandable +sendMessage() { + const text = this.editorService.getText(this.editor); + + if (text.trim().length === 0) { + return; // Empty message + } + + if (text.length > 10000) { + // Show error: "Message too long" + return; + } + + // Send message... +} +``` + +## Troubleshooting + +### Editor Not Focusing + +```typescript +// Ensure element is mounted before creating editor +ngAfterViewInit() { + this.editor = this.editorService.createEditor( + { autofocus: true }, + this.editorElement.nativeElement + ); +} +``` + +### Formatting Not Working + +```typescript expandable +// Check if editor is destroyed +if (this.editor.isDestroyed()) { + console.error('Editor is destroyed'); + return; +} + +// Check if editor is editable +if (!this.editor.isEditable()) { + console.error('Editor is not editable'); + return; +} +``` + +### Mentions Not Showing + +```typescript expandable +// Ensure mention callbacks are configured +this.editor = this.editorService.createEditor({ + onMentionStart: (query) => { + console.log('Mention started:', query); + this.showMentionPopup(query); + }, + getMentionSuggestions: async (query) => { + return await this.searchUsers(query); + } +}); +``` + +## See Also + +- [RichTextEditorService API Reference](../api-reference/rich-text-editor-service.mdx) +- [MessageComposer Component](../components/message-composer.mdx) diff --git a/ui-kit/angular/v5/guides/search-messages.mdx b/ui-kit/angular/v5/guides/search-messages.mdx new file mode 100644 index 000000000..3833081c6 --- /dev/null +++ b/ui-kit/angular/v5/guides/search-messages.mdx @@ -0,0 +1,208 @@ +--- +title: "Search Messages" +sidebarTitle: "Search Messages" +description: "Add full-text message search across conversations with result routing in CometChat Angular UIKit." +--- + + + +| Field | Value | +| --- | --- | +| Package | `@cometchat/chat-uikit-angular` | +| Key components | `cometchat-search-bar`, `cometchat-message-list`, `cometchat-message-composer`, `cometchat-message-header` | +| Init | `CometChatUIKit.init(uiKitSettings)` then `CometChatUIKit.login("UID")` | +| Purpose | Full-text message search across conversations with result routing and navigation | +| Sample app | [GitHub](https://github.com/cometchat/cometchat-uikit-angular/tree/v5/projects/sample-app) | +| Related | [All Guides](/ui-kit/angular/v5/guides/overview) | + + + +Search Messages lets users locate specific messages across conversations and groups using keyword search, then navigate directly to the result. + +Before starting, complete the [Integration Guide](/integration). + +--- + +## Components + +| Component / Selector | Role | +|:---|:---| +| `cometchat-search-bar` | Search input component for entering keywords | +| `cometchat-message-list` | Displays filtered messages based on search results | +| `cometchat-message-composer` | Supports navigation after selecting a search result | +| `cometchat-message-header` | Displays search context and navigation controls | + +--- + +## Implementation Steps + +### 1. Search State Management + +Track the current search keyword and the message ID to scroll to. When a new search is triggered, reset `goToMessageId` so the list doesn't jump to a stale result. + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; + +@Component({ + selector: 'app-search-messages', + standalone: true, + imports: [], + template: `` +}) +export class SearchMessagesComponent { + searchKeyword = ''; + goToMessageId: number | undefined; + selectedUser: CometChat.User | undefined; + selectedGroup: CometChat.Group | undefined; + + onSearch(keyword: string): void { + this.searchKeyword = keyword; + this.goToMessageId = undefined; + } + + onResultClick(messageId: number): void { + this.goToMessageId = messageId; + } +} +``` + +--- + +### 2. Search Input + +Render the search bar component and wire its output to update the keyword state. + +```html + + +``` + +--- + +### 3. Search Result Integration + +Pass `searchKeyword` and `goToMessageId` to `cometchat-message-list`. The list filters messages matching the keyword and highlights them. When `goToMessageId` is set, the list scrolls to that message. + +```html + + +``` + +--- + +### 4. Navigate to Search Result + +When a user taps a search result, set `goToMessageId` to that message's ID. The message list scrolls to and highlights the target message. + +```typescript +onResultClick(messageId: number): void { + this.goToMessageId = messageId; +} +``` + +--- + +### 5. Complete Search Integration + +A full component wiring search input, result display, and navigation together. + +```typescript expandable +import { Component, Input } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatSearchBarComponent, + CometChatMessageListComponent, + CometChatMessageComposerComponent, + CometChatMessageHeaderComponent +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-chat-with-search', + standalone: true, + imports: [ + CometChatSearchBarComponent, + CometChatMessageListComponent, + CometChatMessageComposerComponent, + CometChatMessageHeaderComponent + ], + template: ` + + ` +}) +export class ChatWithSearchComponent { + @Input() user: CometChat.User | undefined; + @Input() group: CometChat.Group | undefined; + + searchKeyword = ''; + goToMessageId: number | undefined; + + onSearch(keyword: string): void { + this.searchKeyword = keyword; + this.goToMessageId = undefined; + } + + onResultClick(messageId: number): void { + this.goToMessageId = messageId; + } +} +``` + +--- + +## Feature Matrix + +| Feature | Component / Binding | Description | +|:---|:---|:---| +| Search input | `cometchat-search-bar` with `(searchChanged)` | Captures keyword input | +| Display results | `cometchat-message-list` with `[searchKeyword]` | Filters and highlights matching messages | +| Keyword highlighting | `[searchKeyword]` input | Highlights matched text in messages | +| Navigate to result | `[goToMessageId]` input | Scrolls to and highlights target message | +| State management | Component properties | Tracks keyword and target message ID | + +--- + +## Next Steps + + + + The search bar component reference. + + + Render real-time message threads. + + + Browse all feature and formatter guides. + + + Full working sample application on GitHub. + + diff --git a/ui-kit/angular/v5/guides/subscription-patterns.mdx b/ui-kit/angular/v5/guides/subscription-patterns.mdx new file mode 100644 index 000000000..eb16718b8 --- /dev/null +++ b/ui-kit/angular/v5/guides/subscription-patterns.mdx @@ -0,0 +1,254 @@ +--- +title: "RxJS Subscription Patterns" +description: "Approved patterns for managing RxJS subscriptions in the CometChat Angular V5 UIKit to prevent memory leaks." +--- + +## Overview + +This guide documents the approved patterns for managing RxJS subscriptions in the CometChat Angular V5 UIKit. Following these patterns ensures that subscriptions are automatically cleaned up when components and services are destroyed, preventing memory leaks in long-running sessions. + +--- + +## ✅ Approved Patterns + +### 1. `takeUntilDestroyed()` — Primary Pattern + +The preferred pattern for all new subscriptions. Uses Angular's `DestroyRef` to automatically unsubscribe when the component or service is destroyed. + +```typescript +import { Component, inject, DestroyRef } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { CometChatMessageEvents } from '../events/CometChatMessageEvents'; + +@Component({ ... }) +export class MyComponent { + private readonly destroyRef = inject(DestroyRef); + + constructor() { + CometChatMessageEvents.ccMessageRead + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(message => { + // handle message read + }); + } +} +``` + +**Why this is preferred:** +- No boilerplate `ngOnDestroy` needed for subscription cleanup +- Works in both components and services +- Angular manages the lifecycle automatically via `DestroyRef` +- Composable with other RxJS operators + +--- + +### 2. `toSignal()` — For Service Observables + +Use `toSignal()` when you need to consume a service observable as an Angular Signal in a component. The subscription is automatically cleaned up when the component is destroyed. + +```typescript +import { Component, inject } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { ConversationsService } from '../services/conversations.service'; + +@Component({ ... }) +export class MyComponent { + private conversationsService = inject(ConversationsService); + + // Automatically subscribes and unsubscribes — no manual cleanup needed + conversations = toSignal(this.conversationsService.conversations$, { + initialValue: [] as CometChat.Conversation[], + }); +} +``` + +**Why this is preferred for service state:** +- Zero subscription management code +- Integrates with Angular's change detection (OnPush compatible) +- Automatically handles initial value and completion + +--- + +### 3. CometChat SDK Listeners — Service-Managed Pattern + +CometChat SDK listeners (`addMessageListener`, `addUserListener`, etc.) must be registered and removed via the service layer, not directly in components. + +```typescript +// ✅ CORRECT — service manages SDK listener lifecycle +@Injectable({ providedIn: 'root' }) +export class ConversationsService { + private readonly listenerId = `conversations_${Date.now()}`; + + private setupMessageListener(): void { + CometChat.addMessageListener( + this.listenerId, + new CometChat.MessageListener({ + onTextMessageReceived: (message) => { /* handle */ }, + onMediaMessageReceived: (message) => { /* handle */ }, + }) + ); + } + + private removeMessageListener(): void { + try { + CometChat.removeMessageListener(this.listenerId); + } catch (error) { + // log but don't throw — cleanup errors are non-critical + } + } + + cleanup(): void { + this.removeMessageListener(); + // ... remove other listeners + } +} +``` + +**Why services manage SDK listeners:** +- SDK listeners are singleton-scoped — they must persist across component destroy/recreate cycles (e.g., tab switching) +- Components should never call `cleanup()` in `ngOnDestroy` — this would tear down listeners on every navigation +- Call `cleanup()` only for application-level events: user logout, account switching + +--- + +## ❌ Anti-Patterns to Avoid + +### ❌ Manual `destroy$` Subject + +```typescript +// ❌ DO NOT USE — verbose boilerplate, error-prone +@Component({ ... }) +export class MyComponent implements OnDestroy { + private destroy$ = new Subject(); + + constructor() { + someObservable$ + .pipe(takeUntil(this.destroy$)) + .subscribe(value => { /* handle */ }); + } + + ngOnDestroy(): void { + this.destroy$.next(); // easy to forget + this.destroy$.complete(); // easy to forget + } +} +``` + +**Problem:** Requires manual `ngOnDestroy` implementation. If `next()` or `complete()` is forgotten, subscriptions leak. + +**Fix:** Use `takeUntilDestroyed(this.destroyRef)` instead. + +--- + +### ❌ Manual `Subscription.unsubscribe()` + +```typescript +// ❌ DO NOT USE — manual tracking is error-prone +@Component({ ... }) +export class MyComponent implements OnDestroy { + private subscription: Subscription | null = null; + + constructor() { + this.subscription = someObservable$.subscribe(value => { /* handle */ }); + } + + ngOnDestroy(): void { + this.subscription?.unsubscribe(); // easy to miss for multiple subscriptions + } +} +``` + +**Problem:** Each subscription requires a separate property and manual cleanup. Scales poorly. + +**Fix:** Use `takeUntilDestroyed(this.destroyRef)` — no property or `ngOnDestroy` needed. + +--- + +### ❌ Subscribing Without Any Cleanup + +```typescript +// ❌ MEMORY LEAK — subscription never cleaned up +@Component({ ... }) +export class MyComponent { + constructor() { + someObservable$.subscribe(value => { + // This subscription lives forever! + }); + } +} +``` + +**Problem:** The subscription persists after the component is destroyed, accumulating over time. + +**Fix:** Always use `takeUntilDestroyed(this.destroyRef)` or `toSignal()`. + +--- + +## Migration Guide + +If you encounter the old `takeUntil(this.destroy$)` pattern, here is how to migrate: + +### Before + +```typescript +import { Component, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ ... }) +export class MyComponent implements OnDestroy { + private destroy$ = new Subject(); + + constructor() { + someObservable$ + .pipe(takeUntil(this.destroy$)) + .subscribe(value => { /* handle */ }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} +``` + +### After + +```typescript +import { Component, inject, DestroyRef } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +@Component({ ... }) +export class MyComponent { + private readonly destroyRef = inject(DestroyRef); + + constructor() { + someObservable$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(value => { /* handle */ }); + } + // No ngOnDestroy needed! +} +``` + +**Steps:** +1. Add `DestroyRef` to `@angular/core` imports +2. Add `takeUntilDestroyed` to `@angular/core/rxjs-interop` imports +3. Add `private readonly destroyRef = inject(DestroyRef)` field +4. Replace `.pipe(takeUntil(this.destroy$))` with `.pipe(takeUntilDestroyed(this.destroyRef))` +5. Remove `private destroy$ = new Subject()` +6. Remove `this.destroy$.next()` and `this.destroy$.complete()` from `ngOnDestroy` +7. Remove `OnDestroy` interface if no other cleanup is needed +8. Remove unused `Subject` and `takeUntil` imports + +--- + +## Summary + +| Pattern | Use Case | Cleanup | +|---------|----------|---------| +| `takeUntilDestroyed(destroyRef)` | Any RxJS subscription in component/service | Automatic via DestroyRef | +| `toSignal(obs$)` | Service observable → Signal in component | Automatic via DestroyRef | +| Service-managed SDK listeners | CometChat SDK `addXxxListener` | Manual via `cleanup()` on logout | +| ~~`takeUntil(destroy$)`~~ | ❌ Deprecated | Manual — avoid | +| ~~`subscription.unsubscribe()`~~ | ❌ Deprecated | Manual — avoid | diff --git a/ui-kit/angular/v5/guides/url-formatter.mdx b/ui-kit/angular/v5/guides/url-formatter.mdx new file mode 100644 index 000000000..a4f642aac --- /dev/null +++ b/ui-kit/angular/v5/guides/url-formatter.mdx @@ -0,0 +1,150 @@ +--- +title: "URL Formatter" +description: "Detect and style plain URLs as clickable links with optional tracking logic in CometChat Angular UI Kit." +--- + + + +| Field | Value | +| --- | --- | +| Package | `@cometchat/chat-uikit-angular` | +| Key class | `CometChatUrlsFormatter` (extends `CometChatTextFormatter`) | +| Required setup | `CometChatUIKit.init(uiKitSettings)` then `CometChatUIKit.login("UID")` | +| Purpose | Auto-detects URLs in text messages and converts them to clickable links | +| Related | [Custom Text Formatter](/ui-kit/angular/v5/guides/custom-text-formatter) \| [All Guides](/ui-kit/angular/v5/guides/overview) | + + + +`CometChatUrlsFormatter` extends [CometChatTextFormatter](/ui-kit/angular/v5/guides/custom-text-formatter) to detect URLs in text messages and render them as clickable links. + +--- + +## Usage + +Extend `CometChatTextFormatter`, set regex patterns for URL detection, and override `onRegexMatch` and `registerEventListeners` to handle formatting and click behavior. + + + +```typescript expandable +import { CometChatTextFormatter } from "@cometchat/chat-uikit-angular"; + +export class CometChatUrlsFormatter extends CometChatTextFormatter { + constructor(regexPatterns: RegExp[]) { + super(); + this.setRegexPatterns(regexPatterns); + } + + onRegexMatch(inputText: string): string { + return this.formatUrls(inputText); + } + + private formatUrls(inputText: string): string { + return inputText.replace( + /(https?:\/\/[^\s]+)/g, + '$1' + ); + } + + registerEventListeners(element: HTMLElement, classList: DOMTokenList): HTMLElement { + if (classList.contains("clickable-url")) { + element.addEventListener("click", this.onUrlClick); + } + return element; + } + + private onUrlClick(event: Event) { + const target = event.target as HTMLAnchorElement; + const url = target.href || target.dataset["url"]; + if (url) { + window.open(url, "_blank", "noopener,noreferrer"); + } + } +} +``` + + + +```typescript expandable +import { Component, OnInit } from "@angular/core"; +import { CometChat } from "@cometchat/chat-sdk-javascript"; +import { CometChatMessageListComponent } from "@cometchat/chat-uikit-angular"; +import { CometChatUrlsFormatter } from "./CometChatUrlsFormatter"; + +@Component({ + selector: "app-url-demo", + standalone: true, + imports: [CometChatMessageListComponent], + template: ` + + + `, +}) +export class UrlDemoComponent implements OnInit { + chatUser: CometChat.User | undefined; + textFormatters = [new CometChatUrlsFormatter([/(https?:\/\/[^\s]+)/g])]; + + ngOnInit() { + CometChat.getUser("uid").then((user) => { + this.chatUser = user; + }); + } +} +``` + + + +--- + +## Customization + +### Styling links + +Apply CSS to your link class: + +```css +.clickable-url { + color: var(--cometchat-primary-color); + text-decoration: underline; + cursor: pointer; +} +``` + +### Handling clicks + +Override `onUrlClick` to add tracking or custom navigation: + +```typescript +private onUrlClick(event: Event) { + event.preventDefault(); + const target = event.target as HTMLAnchorElement; + const url = target.href; + // Add analytics tracking + console.log("URL clicked:", url); + window.open(url, "_blank", "noopener,noreferrer"); +} +``` + + +The `CometChatUrlsFormatter` is included by default in the message list. You only need to pass it explicitly if you want to customize click behavior or combine it with other formatters. + + +--- + +## Next Steps + + + + Build custom inline text patterns. + + + Render real-time message threads. + + + Browse all feature and formatter guides. + + + Add @mentions with styled tokens. + + diff --git a/ui-kit/angular/v5/installation.mdx b/ui-kit/angular/v5/installation.mdx new file mode 100644 index 000000000..dd8e6d61b --- /dev/null +++ b/ui-kit/angular/v5/installation.mdx @@ -0,0 +1,58 @@ +--- +title: Installation +description: "Quick reference for installing CometChat UIKit Angular" +--- + +## Quick Install + +```bash +npm install @cometchat/chat-uikit-angular @cometchat/chat-sdk-javascript +``` + + + `@cometchat/chat-sdk-javascript` and `dompurify` are peer dependencies of the UIKit. npm may install them automatically, but if your project doesn't already have them, install them explicitly: + + ```bash + npm install @cometchat/chat-sdk-javascript dompurify + ``` + + +Then import the CSS variables in your `angular.json` styles array or global stylesheet: + +```css +@import '@cometchat/chat-uikit-angular/styles/css-variables.css'; +``` + +## Register UIKit Assets + +The UIKit ships icons, images, and audio files that Angular's build system won't serve automatically. Add the assets path to your `angular.json` so they are copied to the build output: + +```json expandable +{ + "projects": { + "your-app": { + "architect": { + "build": { + "options": { + "assets": [ + { + "glob": "**/*", + "input": "node_modules/@cometchat/chat-uikit-angular/src/lib/assets", + "output": "assets" + } + ] + } + } + } + } + } +} +``` + + + Skipping this step will cause all UIKit icons, empty-state images, and notification sounds to fail with 404 errors. + + + + For the full setup guide — including initialization, authentication, and your first component — see the [Integration Guide](/ui-kit/angular/v5/integration). + diff --git a/ui-kit/angular/v5/integration.mdx b/ui-kit/angular/v5/integration.mdx index 72f0c0dfc..a3855abfc 100644 --- a/ui-kit/angular/v5/integration.mdx +++ b/ui-kit/angular/v5/integration.mdx @@ -59,12 +59,12 @@ This creates a new Angular project with CSS styling and no server-side rendering ```bash -npm install @cometchat/chat-uikit-angular@5.0.0-beta.2 @cometchat/chat-sdk-javascript +npm install @cometchat/chat-uikit-angular @cometchat/chat-sdk-javascript ``` ```bash -yarn add @cometchat/chat-uikit-angular@5.0.0-beta.2 @cometchat/chat-sdk-javascript +yarn add @cometchat/chat-uikit-angular @cometchat/chat-sdk-javascript ``` @@ -80,7 +80,7 @@ npm install @cometchat/chat-sdk-javascript dompurify If you want voice/video calling, also install: ```bash -npm install @cometchat/calls-sdk-javascript@5.0.0-beta.1 +npm install @cometchat/calls-sdk-javascript ``` --- diff --git a/ui-kit/angular/v5/overview.mdx b/ui-kit/angular/v5/overview.mdx index 038842d0c..8e4abab02 100644 --- a/ui-kit/angular/v5/overview.mdx +++ b/ui-kit/angular/v5/overview.mdx @@ -1,12 +1,8 @@ --- title: Overview -description: "CometChat documentation page." +description: "CometChat Angular UIKit v5 — Pre-built UI components for adding chat to Angular applications" --- - -This is a beta release (`5.0.0-beta.2`). APIs and components may change before the stable release. Report issues on [GitHub](https://github.com/cometchat/cometchat-uikit-angular/issues). - - CometChat UIKit for Angular (`@cometchat/chat-uikit-angular`) provides pre-built UI components to quickly add chat functionality to your Angular applications. It works with `@cometchat/chat-sdk-javascript` and supports Angular v19, v20, and v21. ## Features @@ -66,6 +62,7 @@ CometChat UIKit for Angular (`@cometchat/chat-uikit-angular`) provides pre-built
+ --- ## Resources @@ -77,9 +74,15 @@ CometChat UIKit for Angular (`@cometchat/chat-uikit-angular`) provides pre-built Full UIKit source on GitHub + + Interactive component playground + Design resources and prototyping + + Upgrade from v4 to v5 + Common issues and fixes diff --git a/ui-kit/angular/v5/quickstart.mdx b/ui-kit/angular/v5/quickstart.mdx index 70261ac22..cf654fd3d 100644 --- a/ui-kit/angular/v5/quickstart.mdx +++ b/ui-kit/angular/v5/quickstart.mdx @@ -13,7 +13,7 @@ Follow these steps to integrate CometChat UIKit into your Angular application. ## Step 1: Install the Package ```bash -npm install @cometchat/chat-uikit-angular@5.0.0-beta.2 +npm install @cometchat/chat-uikit-angular ``` diff --git a/ui-kit/angular/v5/troubleshooting.mdx b/ui-kit/angular/v5/troubleshooting.mdx index 97d8ccb51..74fe7de3d 100644 --- a/ui-kit/angular/v5/troubleshooting.mdx +++ b/ui-kit/angular/v5/troubleshooting.mdx @@ -55,7 +55,7 @@ description: "Common failure modes and fixes for the CometChat Angular UIKit." | Symptom | Cause | Fix | | --- | --- | --- | -| Call buttons not appearing in Message Header | `@cometchat/calls-sdk-javascript` not installed | Run `npm install @cometchat/calls-sdk-javascript@5.0.0-beta.1` — UIKit auto-detects it | +| Call buttons not appearing in Message Header | `@cometchat/calls-sdk-javascript` not installed | Run `npm install @cometchat/calls-sdk-javascript` — UIKit auto-detects it | | Incoming call screen not showing | `cometchat-incoming-call` not in the component tree | Render the component at the app root level so it can listen for incoming calls | --- From a4e79721d94ef8a55b2853cd3e63048401e6f56d Mon Sep 17 00:00:00 2001 From: omkar6750 Date: Fri, 8 May 2026 16:18:22 +0530 Subject: [PATCH 08/61] docs: fix broken websocket and ui image references - replace broken websocket lifecycle images in sdk docs - add shared automatic and manual websocket flow images - fix react native ai chat history image reference - fix ios message header trail image filename - add alt text for websocket lifecycle images --- images/automatic-web-socket-handling.png | Bin 0 -> 994258 bytes .../manual-web-socket-connection-handling.png | Bin 0 -> 884870 bytes sdk/android/connection-behaviour.mdx | 4 ++-- sdk/android/v5/connection-behaviour.mdx | 4 ++-- sdk/flutter/connection-behaviour.mdx | 4 ++-- sdk/flutter/v5/connection-behaviour.mdx | 4 ++-- sdk/ios/web-socket-connection-behaviour.mdx | 4 ++-- ...naging-web-sockets-connections-manually.mdx | 6 +++++- ui-kit/ios/message-header.mdx | 2 +- ui-kit/react-native/component-styling.mdx | 4 ++-- 10 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 images/automatic-web-socket-handling.png create mode 100644 images/manual-web-socket-connection-handling.png diff --git a/images/automatic-web-socket-handling.png b/images/automatic-web-socket-handling.png new file mode 100644 index 0000000000000000000000000000000000000000..4ef8215fbdfdc3e3c3b8c5b6438893505b7bcd6a GIT binary patch literal 994258 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

}-9Tt96@M(0MBWy5{%bLiJx=>Uj5*qi6xkd?V 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

8C6Eb{KX~#NDRbG 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%0A#wh4_yUl-w7Z9m;q>< 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

}xJv4MGs7LM>pF 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

izH$*DfiCcJ zr+b9-LOH%(B(^K$+EOlc6DZX^`)C?KW$qmddt6f1l2VXaNyj!-M)65u-wij7Kx(Jy zRxBjI)oXLkid?yP;*6T?9BL_F6reVOW#K8&JTgHzlZQsSo+oImV-IVe*AWVZq9tp0 z{VEK$WSInm2i+b#SnTmJ9#+IOHP&CoC{0)GpxAn^^FD59E_OQ14jl~OOeOU?;pHc= zJD|kIBGBUS(>WijP{y-E!Pq?`rOf#_%}!di!M^!(7#?+_u^BKVqo&S=7s?J&eWYCZ za!+3Zph5H&MCNaqHXk@Y<~eVOIHC-0lRtImTurfqWH19xE>H{QKf}Zr%5)el`@?gD zB>||o;e?Xgwc7}-QxxU`wU-{nj%Yf*%3j|i^gOS(j;ClkCM8ieaa_bgGlxOVIgPC) zu@r$F%*gh&QADVuvhRaQa?gJu;>#_*ABs_@=NLoN1uAxLell%YUU1y5`dFBUflzL__b!p>KrsO4tdY}u$u>6tL1SvN2L##e zQ~Tg|ZeRPt?H7O2UU;9LABpTR^tOi7N}>!y&G*|FbzCVzIW+cS*`})JGqPgUlY76G zpN8$)p5l}7uE$JWjov~JHAYTVOmy4KM0DFmd~TVo)1W`(sgeMqaU`^e3_G4O1xr<* z1C7cjF;m&MUG(%wUi-R!>9e;lzyI{^_n-FLeK$Fojd#UzRyq3s_q-VRBh0+p*};K4 zC|umH>GqUz2;<3W2IBFX`zTcchA=RKo$OkClnPG{0_Zl-zgR69rSO4dW`N@ANVrzR zWJj^v{Ic9%wC{(HA|5Jh!XhOxG(HcH1m`XkUkRTxm!@TWy#3H6$)qGc!^hBHdDP$< z%C*r7hiWViGGyC`g-gPj;~=&gcc8q==HNmC@DB+h3W>zyOQ#2S3#3PF6l(AwVmtcMK|$z!R0Cb> z1k8G|5MK-MiB7)9fLNQ2x;1WV-f8H7f?h+%;x&<@3QI&J;#q-Vf-|b-@bT^0gM2>D@|2AcbTn%i*VD9QlRhX;W$4Qu)*b%ZAaz}b}rSK7*9PhF*<=LPYz zRhJ#)4QeKj522RMw{AdO>*24 zY7QDMlQqJ_YlAV=DH494-&?|5y^rGoB+lX{QkH!baTr9F@~Oclghr7D2ko$60mgDE zjnu%D7pnnn`AkO;PTFCk}vEt<$={WFoU46J!I8i(mY{9vm+H1-A;0Qvp;^aee(Ox|KP9Re(5LV zW54#KD#E9?pir(F2w%-Nr`w&Pbud=lu&{DYF}WXo`bd~lp*-NeU1a{y<4XgFMz zZ7x`;nW+i}4bkdJ}_ruf4F1u}8 z5v8VlD%#}XTY+F9%EYvCd6B*fr0U2Oz>)YD5K~9mcX3Eg8D|R1?HcILmlhOe#X;Dl zT+aiG~BKEWGs(&K{Bp5j7<5> zK}o2Q#dv8Ly)KvqqJapG)zQL%}#8KP83wn-`hPN0!*mTuZI*3<;Kw!J!M-H%GhBV@RoDqns#&s&?(pokV&q*ttTw zM-3F>fu8=3YArl!K%qH1v{sGV?}wXY4U$rLZ^<}3<9chU5Fv#`TLAy>ego7Vh+E=~ z!d9a=Q@|@Voq)ci+$z7aC=5>c#SqyHA1S+@tnLX-1^Y>=At_RNt`({R*CZ&4o2I43l;DZYZ7LxPbf4bb5*%uiA|l&1+gz2Ro*9{- z#~=$TrHe_2?(74IYT<2OdjM5Hs=qtRAiK;3lwK1;6^cp<241|YT800#E zTb{U^VGsL))UiC85?`f5vFIK>AT_={fu&Gf4*SK*ZxulNl(NWDZFJczC~Z9QGc*q} zS`kxE{fu`y)P<6*ja%EQ(?H4bHk98v+uLvNFMnYB$Y)Mp_=)}bm$vtPO64-^jWv(i z%S7<%MCX<#&@p(osOJ!TXX2P~cyKn-D~@EW>gI9mNI#6_Z8QuOcQ9jr?0kJGHzfq2 z06}vmJT^=X71n&o&}Bt`y+0f7!+^q=s!n`44P8|cv$5;yEDs*&&-~|?*S{g({c9h* z^d7yvG1){nWO(#7cM0lDQh=ughpAAHJP9rcqHF7bo@LOvP-&x@q52ydBXn`(ga#if z3;5#fERN9l!+fp@=5pVuZ460X*z0shMA%Jc7l2pDRd z7%lo^r-{+rx{hW0JX;t;J8@|D*Mm^gvWbChw1NSuW^Jr-3?rFcg$hZ4@)l5W4Ldez zwD?|VDLyDU0Mx0~na`!n!9sL%n!9ScW%}k^2*t_}nI3lq;ZL-Spos(C>IVv{^3tD0Ybn(5o%S?Ww^Ykd!5DeuuJ%j>n6X;m{;I z*W!M}L1P;xMB|x~oXw{lPa)KKs0#wO1dh+wT%9IZCZ#D6PXkdrv4}&CSXRHW1aGO* z3=}0lv}~E4cMc6i7Y!Qr;8HV<&B{?maDugjTo+p|$1Bv6#G5eV1KwNfUoQg8ky zOO?a%clPgCb>DLj@A5jYb^r}V>TQG8D|xg+Fw~KUJ$rHY6M2mgMi}?{mra1h>qy$q z%@H1eV6PugAmE{VXaCeLN{U*Q0UfqGyU>M#H0qPE7{0=rCm>PmfL z?`u9iI+n9q=10K3%XkmROuHha$svlO6 z=VO5kjNU_igPidNGr|;)&wnf%9*Ca%Pmv6aJZN}0OL4R-uCP-KI(uv;7PZ_?GkwM0 zTAZAU!ucRYo~q+$prt7y)}|cuC8faFO?BVx=EgqqnbVuE%TNBNx8M8l!OI^}+s!Vq zz@3?rdO2ic=FogN=1*M#D$due)OqlX=rBLuZ#4r8wam%bbUENPL=Pn=r%=!Hv*>*GCWNv=~ z(+d!YfrmY4vq18b8>#T^NM7l+Ee*p8rOK5$|P?s8k>VoMs&^~QA03_9`?i4(J8i9(w-hlVZ5bc8S-NkllC?T8YjJDACzDpcHZ|*g#VJue?R9ive9m2K z=hQq~JU9_yZrnPt`B^!JqS8hA)99-1?nDGyan}6|lD;VJ9+4F{SXxUD2n{zqJAnm& zdh!t@C}+#O9}zuO)XXp^3Ti;$VhgozmGe99Nzn7MrI7mxLd-jl-wK$`_^c*~Dtl`! z85!_v5$g?<(e6FHexXAm4gsiEBl!RqIbIgkfv{QFkm`6T3HJyDfth&>FYcvrKvK0c z^a$UR-J7IYW?zWJMih#$oPiGD7~gk0)K`~93E>u{TxWeBP?WD0HJJ$I)X1HgS}1$e z+S8bme+|MC4};Gz1Ye6!25|P}t{SmhDZast&l(X1%NTbx9eN;o-9K96!Z~^f_4PwY zB7OD2E+)`Q{{m&g3uj-gg~celIff258M;EvQKh}(2LB%Xsd-X2fI3)~33h<@QGOY@hmdr)O`;-}w*U{>%?P{EqM5#B9G25z`a7mVg`- zM{YRqzI9W}&;)(5I-kjq+t1|DqU-=+Ru=C(GFlQ}eAL`7#e1CFsb;nuUZo)ctIGU= zF_AoVF|5HS)U^G_8%h5qW*0Hvbh({GM8Eh``_KQx%|}0d`sk-mqPy+${s7ByQ0|;0 z1UoxLv9V2#cV!HP(Uktt#Rz~lr15?irzDEQ$PWeBHReqT#8T>$+Y;v-Ad;v&GOM(X zDfyjKLf(ce__cY%d}FX#H>L%HC*UKFX&B>`q93pIwHfTV`c91_`tp_^ZFXg3vB6nj z-_13(JTj+*$;dJ|4N8t_NL>0k(&)IytsQK+kPC_5zybFf5fd~Lq8DGP;Efn#o|V&H zHkc%y80m_JIBj#pmH@9){S@emM*+x4x_TjQj?85UR{V7*4g{@^|6vp`{|y6N@+mxe z${oO0wEFq5I(KNU3oGt$L}Abl4oGF2FUn^6F@!+|VG;x!S)kup#rIAJ^o16k=CYA` zbfPC3cp3;JB_0-}AzIvn0)_t-owzoSA@SdFvoeh$sb)Cn1MHB;? z-nGTE|7r58tE4S00^7{UQOckpHr{F&XC?IVyGk07Pn-R8g(x$u+W5|2JB`dSA>Ud9 zs4Ep7xR6+8DzeNP!+Wy_7!o7ATEX`q!-~+@%5(<74%*p7c$9-!(tjIXMMTvSS)u_q z?HVo^{IY^u|HApjh**GO(y!jd=m6j0riP&DnqOk@GJF8aor*t)zxyn$NzR(crLM?z zN8|pFhlrAMK_@5(tMDIP`;YOYWGbqa3QSmeB$@wo3yclUI=teEKphW^*0CFQb&7*TKYiac|9X>rJEY3!N1= z!cqvduUfZA&Xj5>37_F)oJO|xDI^t$9!Z4~Lj!HI-^O*!Y|w*<%!OiD%QYJBk%{%3 zg6}Ug7)4aX?IgE1_TYhj*AHwz{kKm4;IF^^t*<}$)USDPe$H-h#BN1YWD7-rcQA`c zBofhXE+hAp>P^vLL1haY!WEi)nakf8OF8IV-Bro|#zVe03$G`jZ;ZL7b?Q*NzS=43=n@&$bxh@A8hy>h2 z`ZrjTYfPvB+d91<3`>Z2Y7rkkFvR9+Nd}oc87_QfUNpp>Xk005bimL^y+9M7oxwiZ zd#rwdRplqH4)&g_X3n@O$|0B~n(}AKLa{3TF{Dgg9VFsbM5NW_7=?>Sf_N8l4$bJp zbE8B{am-U5SVY_C}+xWm*PXYBXh~!7(IWC{&|oWF%Xtrj)(-Ft3WR_%b!TXr=&;!WtW% z_%K?19zalGAK^Q?@cW^B+%}WeiAg2vBZ1xFgwtR{kg(d6Vuj$;0mD9Lo$5x8mLvwd zMj#;(tzDIN6^sT=iVmpIjheFv-!>7E zeV3GojO`z$@xHN`sOYYyqWhJd1Ww~5X7@p8-dsI~4=dj0p{TH`P-&<6hRQ-OqJz$Q zry1|WFO7xI2x|gMsV#M!)QyQUq@E3|T?j7=1&N=IO6N393VsO`9l;@PVqmbM1vS4E z0+*9N0l+|Hezb|iskxvrB`kxrquDg<%?QZI^$zcpm!2w%lZxnbACU<9xAM9QpfytH(uNF@fa}y!NGvB+kY*R;q zd7@O=B{@5@1%8f{$3rFK;=9 zCZO*cMu8qU>aC35z6_3wwjojDHu!mjUoPBbi(|m+j*+mH@_nP@TV?szqx<% zd(R*E*d{8M%dRJ#He{0`j-W(L_KZM;91>^w#f+l4V44~nD8%bXEx|7 z7D(xybn&uM4}bI8kd*OvE(tYcyK0r;p7XPnM_HJxkKae-yfoA6juDWAO7DqM;F`u? zm@)+o%HhNYo@IRmD*x-9`dCLID*FQXJBo}7x9E}&Qg9+514CLwwoMk})>S&6>V&ZQ z;^qV-*;3hy7AjB??aji91&Y6+&Q;>Tx?#67h zl!{ujFLi%2i3@!nqOE~oTr734j;#=ghe)sG;$lnf)}n=WjkEPt4~{!Bk%0HQPc3QG z8hVwHo!Y9D!(W)L#lak4S}mO#2|)zaogXcvxL!{b&wZEDNf7mJf-z4cOjIx=SjJRk zC(bg8iG{cPN3t+Fx`nc+TNp}cmII9l@uDkLQ9P2#TSE%XJCDuM5vw!^XNx&hLcAdL zOztWBLf2}w)?cU)njvF!*H-bI)6Ffv@A}DK4ers5>=7kXv;IYk%}_%o zvfk|Ly_6tBTMI1JY0LFT$W|sE3h6>k4KkJu3_NIPcb#yh%^sgILo|)IFjo7m;tqN= zIzu+>o|jkaaA+OeP6{~?b7z*k$eey(US{f=73ttv-S`d?Z;Tm4!s)B z=W^&eDk$J|bxqMenp7~@j69j)hWgNJvXC$KdyiKT!u_BkJ|nddz)pKaTKIh^^@rW& zz*ZL1n#KGCVxYb1nvLq8)q(3M%4H((Hh7Ba zh$bV|Fe;|(&WoiS@rl!2&10ivzL+l|*jYFPXK9ei?Mw#*wJH%Q0;?H>1&X#SSX=mU z3Gt9Kh`AHDV}rVxNs&ApG2R2kipAQ>-ked>U2448K6;0>zqy$?J#wVOa;A;`J4NQV znU6WwQyPX?igB70Z+pAyw0UXT9ta4>HssMq+YsTU2;u!<@oU=TNspx-Z0OHXnQA}| zcS8)-ow7d>5j?jF=@lIJl@oM%r|?+6dbrolTw;7sL76PyN>P&hL0mnFYQD{=#PA7$ z=Nt*ZAtP1!5e)#~dn&L}h3$v2NdS13LATDhc~Hpd>A+b@f(clwkN2)*=xVdZX(^xp zH}$Vt5e1qFM$+ZZJ3e0URRwgl;K3UA;a;6KASU~hE`!BPD=Ew)G0O#GOY=>R0KvWb zjJg`jv3^EEYGEJ=F9Y{N8Qdp0i4o~;UPP3Zj8NyYjI5U%+wVgGyX4e?`*E|3xYfdo zILaTDACdARfWNC;RL5fU{A@YpUAHVtQcvP38)dPAkwE}XeoAFOFyAE@?s425EAYuW zt#_HstwuI6!l?lhwDJB$?O&wo2@k@B47`tS9*_#}lAM&zX1Pz}M8i88#IXs$En}WBr{jIcX&k%0{kp#Oro8oy%eTIM z`S!Q=)5FsTKfb;CiPQP9TrMUiy3JF6%xs%;cJci{%u2)L#e2$2O!n5cfH!V0pD^-tgEkYcf!Qq6Ol#w;p!h zXO&u*cXV+|t<$jbQ2Q(}Ukls8-;`BNAK57+Tw4n{iodqzrxGc?Ng2EaV@E$nRhBX8 zLy8Xj{Ls7;EjxlVc+Y+OI*5zkK})}uV~1-iWuL8Tkw2mT++r;%{-*tmJ_Z8D9!iJ`vO{T^qOFs`_bPmW!|5A~cvHg!jXywY&c zgRpC`@+`{4+i9kP+iXk8Kt}(%g;=8tf=q!vlS=Cjk$geEFto}G`HkcrFif@bWgD)zK?vGA!tK&_qYDYyvyv9`I|D_cXc;7UE=X}AP@%uNI83`>Awe%o+8QiC)9Uv z3-X)i#1iAfuqE3)!w6lYXVAF^$oAg}bobnmqV3fS%$CKHkwiccZr1Z^kPxx8K>Vs@3) zitTIsgqZc=XavNV_-X}1Vk_>33BROP9<5lLhM=wRbzJMTYHMVYan#JU_DK(kX#{O3 zIe}Z}1{btuq_Ev!fX&EY)So{XFWUp%Z|$v{%cGaJPyM>nD<74wd~yHfFJHd-_ifuR z&pp|mytqAimp*u;+eu_I*<{;Hw>ekMcPVFYlbGRz#s(rA$Lj3Kpoqit9_vgSFTcs$ zrJy!;+~`Skv+X=yBH6|v<>OfW$sWE!u0yysv*y!I;e~Mr(|Kd=^nw2-<<3a_irFr@ z+kTO^-?VSPw%^=}>i+n7dH+XFuYBbE1 z&Q$;iFGF!NChj0asPRa|Yndj9#9^TZ;7d`~g&Gslk%07Ro5~0S)hX;w8*YFH)NB#U zlqoUoz|C7}JOGHi$u5OqBJ?TSxQ=Ghdp(+w_Viw6j zF9PwSvxIgS=_+U-!Xl)D-zUWJICOoeodKB63&jvoFSpn~MpXCT)MGo>r!`ro<$U*G+BT02nhCP-uq({#>*fEqShV%%Uwk z<+$fA#l>FpAB{#Jbz$^zz)NAe%fQ``NdC-JiVy+>t^i+sw-xu1;v2R&eYR4mBpq>> zw#Q-HI7I40zvL&oFo{e=f#mSvDyTv=`hlj?A7dM0>T^184cf^e*kWf{@Y5;+xE-$=sb^BzPJ+aIy(yS&K!`a+BSfB3uWek+-;Dm zHpOPUn2poT_u1)|kA}qRtbKe$+Jn>Zp9_3@z48lhiW@<(5VcCan%`=<@kJxeM zw)qhA@~)|S^lyH@9o6VR$OwTZ(oOA2`PmxrrD&5pFE7<6R-REhDhSsi!&G9q*_FmX z{$w0l#jhyUm5)HzVA)V^S%qQHS_wRpIHOxpBMhir_rm}U+DQrxKEz*qJ#XGLigT#O z?(M9D=5iJ2JBk%ZD}xF3>3EL@G-X;?wiQL*fYLB(&k6G=7j#T2a(*8L8_3598a(rJ z`g&i~tu3B`re}9Yz+m;0l~Kll&u#3%$O4xpGeLWq_8Ay+Zd&iW1s8STpbT?>%{0Eo zW0UdXuZW1PehQ6;nQ}S!sQC8obQY?mIhPq;XFk{^=5DQ0{;XgOVeA&ZVo&~vccYS= zSD%ntAr1$>cqrZ#Lk-I6)*b|t);n%`j}4#)6b_Ff_&mm|;;Qz*+Czp#w2;3G0EO9j z7N;Zb`6XR2h}*D5M{69wX+pkn3ebj)qav+h=|xxtZ^ld$-g3UX&DPqK_(}k=zPhg{ zL#;IEsnhG;tbXl6pq^GRX|q>&mpJF@Jw5Ft`aU4dTY|S-STjFl1`9%qY6sWh%ekFd zbnjj9)-9CUN8;hZ_Xo7ZOAU6ZHM~Wq7NKw27C;G?uZ}9pC>A|tJJ2Y;r_CY`95z#g zfcF_JtU_M$vq%D7yVJx8q-iLy43%K4AZ56+`W^yMBUxOzuI&`2=g`k$synJ%)aj@iUkn3Ei}$6y063*M=VP&%kXgtc zY+;~SJ@@XD zK2XuIpL4{qD!V6c;JCMWT%e;2%)t_-=hQ8dQ)YYSt;|ehA56{l^XZJw^5{8z?s+{w zCnMkX?AdN2+eya>L1LntcQMBPo|&6^ehCg@IQ4Kixz*VVV$_0h%0a_ZDY_jxxG7|q z|52{4qC7PWGVgAu-qAAG!R?HaLON&<>v1{oaO~{n=b)cFX?yb}@zC|{+JzgE;U3i_ z2YJvS?&txZx^@Wg)?qw$Cke@|?AmfbCu&hxa&^gzh)hZ+A=nJoOe9P)(~m=gF@=Nw zFR1~VK`hj3?*Oiq)ewG-%HGC;ju3I%w^p`qib!Tr51PRnR{pNo2*ies2VQ#37n;}s zDKgE`X3Oq0YX+c`-J*qc%Zm$CVTzq&Fx<9Qf~tXVMAZEtC=`nsD}6JXcr}zpLNemE zfJKzcpw})bH&vD~88GPBiZ3?0MdS2+zLgs9ZnGG1T`+~yrU~s0FzhR72gWr#cDvST zuN4cLgo+O+AvYmf0Q?c0EGEiLUBS_+gYH6uj#Myl72dV2v>t72Y|gAc9idv}=B)&|+c{bl>hP6N19#ZuL8(Cf$mRD7BFsjZ&&~>k>cltB*B5EMn?lR1yc{=`2 z3tssGX+?Qp{2ek}tD05$zy5m}ysQ@-8YFoMG7y^l~+Pmg^H&#(8(g0;p2At?n zHN!~d3S%pYze7Q^N%$EyJ{hF1NhAP53vEM$vN3-zvv@9vI5c53w=9yQU>n&5aVc{X zo^5Qzoz7c<^fK05Q8cj=Q#B#Asi}?x#c!q0h5_F+RSYHn!Aczw$sTr zm3_CHi`k8AYNxY3|Gw>A4^FDOsp(1Q%snk`0fXg^T})KW{P%RmyJ03`X8Zgt`S~ji z^SMqVQMv3QXMOU5{>}f-8-MFBzxKO-kaS6Ja~!9dO^x z5q0yJAsMfjc>+ky%=Wpb32oI(RBXT4&1JqMvu%1hxwkdhb>BuJ&}73MB?-AELT7CB zKmf(Be#8k=lr`uMo-p{$bV&IUSC~- zIvI0KODidNw^f)>TWj?T^W z+2gmvSV-AA0HnJU_mkTe&O++dr3B`ZA`!-iJQ&Km^giI6BNNdX-Ohc34mx4&;3M!d z6lOTDHf3MgXjU444!5;J!^kDAF!Fl#0Ty}6}tpE)A_#UAXv z_!SXpzrjC_XcQq0kLtS$1znFTBPxr+f@6yM z$5uV(j81qnMr<^XAq)6~Ogh`}>HxgytKlMLCDfu)g{_R-dNYJfB7Vkl?d$MLGdBnq zRmA)Vnq5O&DQK^)?)%YX51iL(EQ|5(>2O=H5d$&&tV*0B#GJBdc;F_ni7RNP79o4l zOI$iKJWX{4Pu`R`xrO7PDeS9f^dlrdcA~x^&v5o(P%?o=&G(&ZsKhK}ybTumgg9i! zjWOlnlBU)7`poGA*TH@Gt-YpFO?=r{GK^j&)K*=>v*m@bybIx1PznR4I`;v^aID)L z>J{@2O|&?qqm20sCc5WntkI)I?lU$z<+~2IxPGcfPReTF#M?N1>Eg<{%P#&KR0H8v zHW5APfQ#_FV`%m{LBe@Myp?*WK&$ltz~vr)xXdfYa%`(pz)D4&z$-qoFG^mE2iSv9 z31d)h{SX)AmP8PX=&Tx~DB-Kk3ho68N7x2dnbTi?FN^n_u(MrZ%iWZ$mRmgZX)=i6 z1f))=&WLf1t*{B9Eertep$Z()UmWnnR&NF*f|MuA*+^$ZdWF$uhKWaU9tRE>5|ABu zQIquh3SvFb_{9^jw-#qXcUpwN`_^iUJ*Md8)MmBmFR=qlB*(Bs7V+;<=DGPA?az-wnbCh?v-Gp3t6pSLGpe~()TfG* zc$T9c=^PPJe581n*=qJ?`GRh+(->Pxsf;BS2|Tm%x!mnTAH*K#jAYvE&PUmT7zv`5$zsreomj@;bL0deLw+~#KE!?hF#UmS#b$zuazwx z$BVde!m#9h#t%oJP2XeThA>>J1(szg*D^eB9{k%dn=($<1eKl9IxzpU);n{F-S|p2W z;vp~2;`I*;wNI@Ixn)y;;W|wV?5Mn{d z{5A&yetl;jLXOyapC%Ofhde&8JIinuDx0XOZf3YnBHKx9-^K#P_-EVv6$1+!RPRNic5|Q&Og{Ug^yn@--LFkNZ6l~E7J6S_>p)%5YWfix2flYI`BCjd=+vT2M z583Xt{uW0&&pAy{JoYWZ<|F^D6f=Lr#nYV0+lF6I61XZe;}*snCogx@r0K-uy(g?) z(?j<0?$4N^d*te1Q?4i+dKHJ7LW^K=ahC=%`$t-b*|Ik82 zEG2isuOCS;5^}O`{lqgN!SRN42{)l+?DRv@w=c97g;j@oQn!O3a55}f9v-+o+Q;Gtsg3vrxHZBAuA$yWkg0TIx`53-!F^X!376ye>ORI!-x z!26nsqx4FS6eB=%2Zz%+8KhSCOSxt+tY;S-T zdFM^(;Xt+uVdE(6sH%YfTE4`|YYzBmA%U=qz-Kk zinT`_&5?l0X%i?csudP@H{eM zea^q6nP4U1_!kWZ@%}-fBQ9mfVvP%3Io=dW$1P3S>ijt(aJ;K9`GmH?aCtna7iO^F zTdqy;9;RZU)%2p3JdfotfHfGM_-NuoRjxY94o9+5jyf~c@PT~@2>0Phl|PojVb{2w ziGdJ<)`4Y(X-gfg%L5k+j;jno7FmcT{vS5d(;w8Z9Xty0Ch^D9fuk;z20BNlK*qSW zIFSopYc9o;!|rB}<^r>fmM4BI!mY4?BsgBdju)m`{k%r!w{yCF)}k_P)Mndy_3IfHn zh3te;({jCv`w4fxVAcJnw;)+iC36bM7ZYOcvUH&}8?5D+rzjM>gifhsG7Xj57stSA zBV9LtJ0o0hs`i4*kcz{F`guS`a#fIP3u}QgbuwVc2B9HCclc=lk$`Gb73o7$U=dou zyX@y=(w?Uc2og^bhe+yAjRqY|sDFZpQH%QUe#dwGX%!1G9Jq;S(17&ukIEdgt&hT# z?*iqc!13`oSZY?MkR}vmSr{}b+(wjH_`&KeG?%)eu5tD1yB7cwhrw7ssHjVPua9i= zJ?Dl?vV!l0<>liT=W?32I(`KzCXHhg%dn(PbRLM~C}eZZq2*&uRp#2}!itleaU6)_ zj@oVk&AMhB9D3!ChTZzey9*=8bg)_vQz$@l2*Bsx17Jc+5!pSUuWu{^F~7F!pF zOdKg#WCrJ_1Uzn=AC{C{kKEck3wyd}NJ42ChPxbaXPb{1_)L+x%ZkP+PqPx$xkf~D zTAjn`U4gQdvomm5u!Y%Aju*D>Mle!nWUl$lDM8!-!%P?~7uZr!s#?MIuIo24Si=mM z=eVX#=X0<Gsuv}>In)#1yp165sYe?-Yf>G)M7ElyDla>(C<;z~0ECF%?hg&;6?wwB^HOce;a$CxL3W_p+ z)Z*}XxJgfXL=~E7^Q4@b?_v2W2by%Hx(tfEhdd@0g$4oxJ&rd`HyrPKYgeL_^H9d8 zFgtqB{v!ttICeM(aRzBA%)L#lEc6aT#>H*5^iGIz6Z1_Derwh6JQD`oD7oIDQT_w% z31neDw6v4!4cugU3=9~8i%RA4L|XFS(alOd zFgl#}GY<;I)Z@W|+d1b6l{$-hoL+&li(AVfphI`+UPzlRaM4A}U6BWJa}Kd-tw;W9 zmiDNs>@2c|@Wshj#Gcinw8KcumJAC8)>IbTmHc>FU-)8r*45yB2?vbTdRt>F?m*t# z%V>Oa!!F5#Y$jE7fl5k$@pc2QE~jNPj+njlwHsX8VG1Cxx*S^KqwF@Z0)FJ~VZJ^; z5SztchxdGb=a3(FV5_!)p5V7a-9yU-~iyp5gQV)TBr(7p#IzwwpXsXLo)XZ zWq>h2eBVWM?su{ITZi+UHnDNqrp=0Y%p?HKW@75^n3dBxNA_G@bY^CF!6+Mt~%`o5&u&Va99R^`+T-67O)ui9;dZZ34xR2-uh- zv)3OZ04^gfU^*$Kig#TE?4kB1BJmq)pomawiX(NjLVg+q#Y*{zl&Jeu&NKQag40k9 zFje?~y6ZUTPJPjFIxB`Y1&0M5_toxaCB$+X2NDsspE9_>7( zmQ!13BxG4wMw(YfevBW7g_9VMowIAuA@X5Cd2c-B?894@J=4t5ibAX(kLU1R;nzSf$^2!^X zK*JQou@CSxKQ`SrB`)Z>&vlrVLDdx_0W8eehZ6Oqc0n)Q39z0-D?sRi*3xXyWXl4j zS|OWe355^8Y2mJW%XbJl96@N(BBUAZXsZNYs>#IX9(`Q-`8e8q4PX(Qa%ZXeyltFm zs@0rW7I20`qsoy#@JN@DWrZoL_4_%f%fUE6-hnraTdV^I?KJdZpGZ$)#VBDLTLH|S zWmd#f6rlkz}YS%6vfg>V0eY0CquD<=J3t{d9s>8@h8S`^%z@;}A zsI;4}YB9MwAfsEXh+-lN^(mJkvA9UY1alLIM*zqZC`%2lOTZ3}V9xG9${Fa@iFG5Y zvIT!s=4thXl7Y3clXd~KL0ntUG`JiSWR3fQ-Qfs zY2FWyD4xexP&3YI@k4GZ*}Q|wqW4P5h=W1PeO7-vY&;u3BXNYF_qm!KAs(?Mv&MB5 z*Yu57FS9yDZyyy2i^6`I2bUittbUy=R3Hxwo$-V<#Z#&VFtXyuaZjCq@z(h~iAUzq zIpRN6G}k7QXV)MVnLlx*B64D3`c|M#wwY%Mu$hLnO;QK?&)p>=rg21vh|DcRuC2Pc z%M(%6eHXb*AyfA@jHL8FHdG-K6e?a5R(H)v99`$*4X-ZS(iV zbsIme=9?otm{7^X-YTT<=!uBND{?T3^z!zQinp_S@gO$l>*x{kxJK=a(E`{d}+1#_M&fPgyrHN-690Q>C0XlGh zqsPTaLjad?2zuV3=xu?Cs9h3fx_AmsDr@_Q6e1_ID%I*w2qgnqvt0j0dhg8Fq@bPH z73=abii;c@`vjc`C36J-DLLGRV44IDZ851se_(GV{J_s_Fm5E0eqPwF4!leUOZ@C$ z6-zAjFc7H?Pru~X)K|v#r}&J4jdBJ&XlXTKH~^-vxa|)EpKy!vB0*3gv^zS zu>v%{@?8TmM&kx!e$a&wOF`gz8-gvM^PNQcp%7Y|j>xFX((uKjZIWII%hLG=V}zxV zCFQ&3Tz$Az7hIdv+V04*D1*-3Z!KgvhsmEP-4+quQIBoP%W5%vPdbq#o!!<3Q2DQt zyxaOp5Fdk9R1TXAI`@4#+@*(I$Kh)#uXyUwlKJaeO`o_0_tZykpmaftaUlj2>W(utA?*F{;11mioX4hh4kiV#q$741!86blS~eb4lZ1Ljf-bEDk6Q^ zp%{b{Q!TRc)dK=5$<%;Q5n*R}l`1-{l<1SRh zY;GL(pJ2%q^g*hpigw!P5)_VouKpI5ium>!`epHvlJON=fAdsjKzPtQTD>^4A~t?? z%f#l3V-EOgDymz|az~cf{A6a6+>TB7lg`%FCWSmRO#V8N%}^@#HgBq;{u{yY#-&CQ zZ;r|ozFH#DPzr(!z*xUEjWpd_NFgp>D)JGBL4g(x`k-N(+licNZ_HB(-7j2mYbaH! z1Z(rC`zm9+Z0L{6$C=?H^}Xj779MNH|u=_(iPpvGAOWvzy{}}Dqu@divnJLbD*=oyo$LCD32SP2@iqP z!7q0X^q_Rp2uX)H#g4@&%0lB?w8aAlZXs&}T;uI1NRlMakcCG#sR`m11r(EGqK?wx zLEpI|R!>>VhTvuSCU0Dc4>wXS(jaUm)UBH`z48zgSw8en$}(RV5Y;XoVSWQ%i5Fm~ zh?zcIy$NbuSu`liNmDy54*EBp)x(;jnjf%tRIP?@lC#-7N&XD!aDG_HrUS zTWh5yTdi=bD1{gvn6-M}v3VIt2j4dONcR`vkyrNmN2>)@LJm}+XW-qI1`EiZO!^d` z_dJbe7yDMQd!iExg|BlD5l2U%$0<7%nCr8aH7lkhSA*f9h^S_mWD&~y;%VQ;JIi_H z@wFfXf9+$l&3;pS$F`_ZVm-zxK1r-`4Yh4`w@vFb6@Rt+s|Uf}dF?EKAuVL(!8B{k zzwLJ|;4Z`(N{49vI8 zNTUQ!0wz3zQ82{e6=y!+;SWgFQ9ygAn^|StrHsVUl2|O{;W#vjn=iYfww3MN5SPqNb)Nk=#QN&VDKdy&Yx{Gh|_O#@R`Cqdq#22$*V*73)Jx3w#!-y*AmU$OxbK>9-=ujaagY&5?W?n4ps;`R!vM^3b z8YiJv??u7!BS$P3koa7p9&EIL!UMpghjeT6@nqGG!q&wR0BU9|_0(aIe z9E#9DD|`peRQyy3G*YFS%>$ZJ7@FjDGR&Ep?MM6>;&S3TUj-1ThbzYk%^wG#LmUQv zfDEV~oZ*%bU`^HXJggFrHs~>HWmhgcyU_!=_;ahGxiS~aPH+}pP`_5cLS8&wqrh-L zob@Up)ASqwOF*>0@REMuk>jDp-`+b6?nKMn@a|fN$fhC!zx3GD6-=WpY>v;X?N_%IB?CXX(c zfn^#FN#_DMiWO0SjkS=YPzTQxS?3W%J;eaZ8y>Q~jvd5M!oP|>&}qe~S88HGmi*DQ z4)>up2c}swBU;#W5E9*6E06YwvN3+dy(t!pc1$Es)k5vD z5#wl>ugR`s_rxZ$o7gU=-A;PBz3jG|UBoW4)jeJ}ler@}z8RkX6Y|*ed)l+5Xo_m6 zh^U#0?L!j71GtD{VL=-*mB7K~8!YT+0#8%P&3N~%vbx2@4W%EE2rqAm511M}O0T0_ z80|DcrCiE)BN8H=#H6~Rw~0>SjTCMSR00z~lQzJIV%^aRZ#MvN9POx(p~CLaa39QC zDa5wt?{I0j_MDycNHF&tp$tcfaQnqcI;S9}Oi{UFD`5BsSC`72 zCnwO`X5+oh+@P&4L6%b}L++%4(Q%4N3n2<`8)lOyJixIzs(Im<&Gn6_p>21V`Q|wb z1!zsqO;u^@&S0df6M=l68K@KHa{LL&ncV9-B|UUE2H)a7QVcPwfs_Eq2E$ULQ(gfr z8r~yXl?1b_TKxW_kb_wQ*66}54$WQQr z`KI9scH0KWZ48GYS9z>9Ivrc(y`)=G4(#_p1FOLmZP&q&5qts$?;L}!ZTIsQ$v|4zE zQbKUZofozBYb*RFAN(sH2UmO6GHOm%A*?uZ+JQ>XjsL;w4R0Ni75a?FjnsgT`&=o- zz%!ER9^tu?1wA2$iWaUyCJ5#aTi3Hb6#wCp4X^;rziTGDA!zGBc|>zD|2*&Z<{K zf+BoCFKT1wM-OkriN-j(BYAVL|s)o(M3pSYFhWBwtO|zJs7U}01G*M2%v;@U2YVM% zfR*)kE`lP(!P?!U;Ev^J)?}wQZa{zv&B{z49uL{^1tWB@s#=OII65p+Uq@IP$OIY_ z*iCmAU|w_Oi=yU$*YqWw7RPu7AM9dbtvLuU~aFR1KkrpJqDJ4Xr1m$EQCqy4QHRviy=1;!tlS%;!)vM7BLV(V5acEwL)t*Vbj=H{_HBF#XvmE1a zVW53zZAK9+QIchK8sZKyMQ9-|H4#ZiY-DZjG(MBHV{fHrSNkOk^T;~{YA&{zV(vY( z>J9*}B#>00l)q+stvzo9;kXb5Jy5%S7tXldHVqbi)pU;2k7=gTxYC{6C}{pff&g6? z(7O7Sx8Baweye~%JLXf@1U4&kS(p<#mk6*Y7+sAO1gqQpgPz$=Kr>)9m1#EhZh6%-cvjzbJXXvGxwu9nQElF79uRgujskpk77}LQULcg0*~LtK z$2KVqcgK1Cum*Uq1*RezErQoCz29-+*yL*BxJ{Nb6(bkul!+G@`aFELER$iku^Lf- zTLE6hwE9}0NlAe$+aDT<875jrW`n!c~}MJt2RS_Z~$~>!ErUkd?RlvfqHTF1@mB{fUZFg z<+38V6vvRzD?ihRZliI13&mBkko15KxlJyNFUwn z76uK<%(03Ci1(hnx2Bb2dcVMO(maxAN6*Ew`fA0=sTc+XE-5rg++DaRqA%QD&Fqke zgz8X}Y6x&&{W`%qDvTP%rXWt{>@$l`OoH2y-zD_PL~+0cN=(%*(>CYTKN0 z5<=0O!OOxe%7{D>N+ZHHD}{?@;ohbBICLm79Ec)`pTJPSOCl381tXJQL!&rYh{2~+ zX_sEAMR|D-Lk644%4~l&jH5-sy}rOAO2}`>N~^Q<0;NWU+@n0YuA9$iN2R$%+~Jh8td{TPUyWwhQM1 zed5sGT0n3Bd#FRsP@tvoY=Ln`VP4e$pa3gsX@e%j^oiz~J)CHt+U)A0iB zvPw8g6c>C2HR;>+idJC@Op!NuU&@yFUQaZ#?5DR~)3hRF_8TymP8Eoc2DRLUg3Zxt zD+~|Ijp)0IlnYU}QMxCV5xAJNWfy#Bqy7JpYU`|Q8;A-J9H&%M4ttr<=Ky4`0(Z64ki(sel$PrwF zmr2$j-Naud_|nh3{r~v4KKIsF&(A;i|M9oqwu?P| z{FSf0{oW5h{pbGGSD*W?$NRIHf7(vsKR*w45AUF9U(TgZHc896AZnHrv8Orj%@NX2 z|1M~mn^>3n9b~5#5SU)so0FH#zU%w`q5KJC7Uo+p+g21R^MWzv6d<+|(KY(6Nk_vA zkCw23=?S2CNdVQ?HlY>^z4S&Zs)@fU+79WPQ zO`rd(M+1jEgO4OqD=H0?b~}Zd)wP<^g1;^bbpqJ!;4=iJprjILh&Wyvd|z!Ni2`Tn zquM)FEZ>p z7G{-pfh8a=8eWB3$QJ{G7(~d%X-WS0U8t|u28P>NN^%NWk{ga>&I(Is+WTVT~0>!~aGU~Pxn-fdU zyiXelU&HvSc1NdLexYiSJ#L-ehXt6AiA?!)%ataxe7dmEg^pFa*X@D>S<_5juC1OO zREv+H4gQ|5PeCvaSjCfgc@D@1Mi^ETnWqAwk^#8hO=jGQS0j8pOmHR{Z&P@`JiSqOU2Mt+4lp9NUNp+4{mR_oLoifc_V7EIbRr>6;|0H)Ewdm! zC@_iWrB=etiP5*@;ThiJWZC_OJCKqhPB4idvVuqH9ReBYV2cn7Su`vRhILGy&?kY4 zS~fE!0KN|JJcM2{m4e-a-}fPQ4KVg8nCWBGj8AnE<#)#+1tJoM$&CFku!$(i5LL#* z6igZ_y6>)i7A6ZvBg}>osA<(-Tg0NUl`XJAG7hvZ2j>puU^D3nHxO(mjKL5AvY~w? z45g#4P?ruIJs*h&6C3;@CnOt;(?T$B*r>gg(;xj@!o@btlZ`aG^Irf1e^d?0f6DLvY**n z6=w*Ac}C{VTY~cgXrcZQ_xxA_<-oX3df>=`9=jsPU}qhO#BSO0^UUM<21+U35HX zzYp~BgB3Srw$INJtgvW&97HL60)KU+%}nY-yzay4bdTkNB}&ieUqGxfxb^GI=JFhf z58~tLGWNR|xNFy;r5siV-@bU5hP8`{TyE{<4{pErk9_o7zwpKV&F#g<&)YM*Jky)E z_ZMG$@ci=+R8C)g{ae53dtdsm{tsS#{=KK$XQpS-u>q)!^Xy<{fE;D<0$;ooN`%f5 z&Cf2G%I0F6?FxjT#lu~78&SP$e99>d^{K!LMYkUraB6JQvh*()f*9J@{q)E9gTe&8 zX(E_TAz~FaQAtX;)pvt?`b=`#wgS6@Wnlq4@oFnwL!)!K1%K_0+r?*Sc{zqWb*b)j z<*!RiWpLM*E;@CoiDSv#3zBPZ;%$2P)Z0<$ZK-iErV$FzI9oos%Zd>>rz#cQjhwPS ztuN!W2J1sj3V|{ScPz~uS=`k&lVDeK)SH9CNjDmHernojtFIOWt-NsQQG>e!$V#YH zU4xT(Y%Ot8yN#jV&*Dd-mB79~BpFFw)dDF3L=(E(x?kVn= z3mm)Zx))d=o+!}<3wqFjAkT3^YyCQT(BI;`l~UZ#9Vvc7;Tw&BRvC}jr8^%FY}yhU z5lB^}_ys^bxTQ!)Y2Yx~QX9R8S6*gbRt?&nSa+422!{@Wm+L6n^;dd}htUCiwro;- zRA+-%?7Ur&M1tP0*ViK^`&i}YMZ(%EWgM}JA{{jAvvW1OzDiIsrV#uWvW%e)Ltfft zzo5?WiKg#`2tx_iO)xg$AckF5I-Y6LzMNE12YmTD;Dvxmg#ywFbPd~GoIt*^-6jhm zwx|?EfW(z6LmCwJzH=d5c?3sHfrDB~^)ULy30yJ=+M-N4&DP1wx)R57SMGLD)rv`N zQKP>v>M>RsnC3Qxn&R^S2M4Qoq=-`t?S_&VY)xE~%*S64rSi;Zu@SZz2&rt@4S6eE zi9MtW4PHT7IchVAyt>Nd<zzxkVupX_SDqy<>)yS+t5#BRUW#%Xl+j$bgzz$w#0xU`+Ai@itwZ zpipO7C-~=GVY3?d%1;XxA1S>rN+}x9^s~{t!v#!}x>3xH1BflW(=?3>-tdo-6HYDV zrck7sGvjTx1aG~}h2K~K&e!p^)HN3b$;^kzhfSuQ>0+#=C?bh2dO` zMo47pI$AxPQWUkBo|wBLFN174SjG8W;QahUlK0GrH_JYnB6P(P*m6-p*ztvM6vRMG zuc%(C5&a_cK%z492Pi<~;zNBb?h&M*e}4mqhfUNcD8dm3z*P)A4YRe^+{YjT>c z>6lEgGY&F(=jP*-%q;w!gdu9AOC&j!Z9G90QLhrUjU?9DAZa;eD|X{GoU}wMqAG2X z@JWg0;KwP4q^5d+*ONSpy_YJgHur3XMDMU!-r`6t&#RCOxMw1ExsmO8`=Q_a%8Req z8*ja7o2cl%n_VvF&0c%sjqm%7FMRLsdUSqH_FFUEMRwJT={+M+*4&!N7g)*%3!oigii)Iylmmfonk6P6^!&oYZe~Y}PJ~f4tmPcYM@B&<(eW^Mx9Tdk(Xt zV+JKl#ZkET9anMsHpI*AD34twuC!O&%S4eoMRcg~SDr+`C{1rAJ!V?h$_8Dj++Q>o z1K*C`{aow@q}1&d@{hc>#f0OoNaI=uix%ld(g7wAy<((?@s;;kOl3i6(JpbDLn?YT z^*E$?5XTWji1>g=bufhLW0=jKnzYV2<2p(RD48!feMo*f4F?0D%Cyw@H zn$Xeis0h6&6Oq_}OS_9x8zcWsKz_xxXJWkvQxNOqo#-)o2tDF+3QPo%kph&;SB;|P z0OJmySb#GTH8`$}qAfR<*oW!?k!7ng{j(wuC?=f^4UMpP>;6hwMPdkm0WA$1v=aiI z1h>gaT5e!viPmO?X^aNcsCuAGt0I?wlWJ& zL$9F=vIVw-fbn_n>zRxCl6aJN0ZkrLkc-{pS}F-c-vOO?XhpeF0|6$+lj^Vn(sG{< zS@EKE9=Me_TCGa{;U*Xh;;_REiK6XZ27tc+3d4hl%g#XD;>M~H>-n119ZC{eMuBnP zG^3qkx?~J1Zug2$HbE+XBc&7FO^q;qF;dIw*uI8ArD3nE3R^Mv?u#J|g6g(ZNDR5` z@J1yR#=82)+Ee2eH*`#!g=P|7=J&8IbF}Z41wOENGmAd_4~&}$G7{Y6p!2&N**d*4 z38Kb9A;NmDVR{HNkh4nz!e*GT7Gno5+AV#Rfu_iaB7JUM z2g~V2sa+P+dX{?G<9%*6c}5r4iONu*-5nno)ZlB`%Y{{EFta568nt*;p!Kgy*P?(r z%H{KB|9nhflvGwLZ`R-wz)m{3*hH9Mhh!O5cXcw7d~@n6#$+x}_?lhWy912GQ??_U z#>)W%-B9c(r?e)uC${e@G^Cy8R!4&^5+4q zF;#7HSg>()M@4m$?Ihbtx0CF4Fy% zG4;Jc9?+4>)`f9_Ho9oRB(G7`F$h19bgs5T&Ye4Tz z0}0kD6O2+Cs}g&aQ0As>KbLq0n?l5muBy#30WdL; z0TQ-U60hK0q&op$g{`#AEPc~T7&O9+3 zc_}#xJB!O+0WB%-HSP6O*R>VwE(%XKBsj7JqVdIcJ}Bi~=utN*5JYVRbJg%u-jda+ z1Aa>oG!eo5F0-LIe!7s`5n}dfZS>SV*E^ZT5L{pRH6?5l)WqD4V2TPIGthB2N!@QY zfWKC^6-4w>f0%KER$R_6_=7a+iX<-avIYdyM-b^DhZA;y(@JS-BaM%Yw`5LV|K>e|6FGu3uL_Qtl+`W zN*7@WbDjp?XpHn$Cdm|?QoJ?19dQ=~8 z+|bJNDtq02GY1$cD-la?1TT0OIgZUs<8AN^qL6IvtM(DNp#FY->mQv8T880MgVm^| z-L|Vp6RB)YN6T$0Fh|iuRc@Zz2R^g?(C>ZU|NXOH`PQ3nz4y77zV+4{?|NB(=#RYk z(ucNZH>M}?biO$fvs-gjGaNAA-O^WR8B+m(71|@e^&24^shlNiK zByzF90);Da)P_K7sI=E5v+mo1k~9TERsqfHL^P3+J;5i*|r> z@JEL9EBs&F=MQLtaZd9HmQ#~ayH(;xtPthED!q55y-eURS<}(XXB@!3wJg__&)9BF zZZsb>IB{HdbxW z3_9U2=PU4KKGcxy^(6h2jk5JlB?#+{9hBGM&SouJ^3o<52)H6B{m~Z)K21KwS9a!P zJNTP9SsRpEBtk)Q-mJmvU@`X*kB(x8at2<3jpbxOaxv^s@Ph`0&Xa-wR2+0+3n>K% zE|UT^E-Mn+!qX&Or9IeMt<+i=hIqeH#ofA#-dGxm4<#|`&)LQ*t+Pg87{x2ZblGy= zB$gN9zxf+ZDk~abu!7+V@kYG|pFtmuo%YHv4isJ`uGZCs~)1+j9u!3~2al4KtK~-oMlt|2g zFM!4ZW0j3M-j4pK!5TY_!jRO9rkpkfl7>mjQ|tq&lPZ=vmpyWn;QNE;p$l3(V0WJx z3aT$Nu%x5KLT>+6jm&^TY0T^qWaDt_B8TF4wrTt5Qy9f#xg;G)T@ z59*DU_iNS-t(vj&{peC+bE@^VA->w?opn`6xes{rLpG}Fv^mUC0_YlzQmjt{yN=Yt z`Y^k1HC`r=@v6|&$vBo~-V5I~x&@C^SXITyrU~^>%H)gz1Qp|^fVhO$B>Cv#$L2)T zxb8~r{9FdhjiCta@N#M^=lP!{y-Bw1vhQ}e*yUoE-Rxqv+rFEay#2=IANzyv|DNCa z!dv!+oSwe%)*CA#*g+I3}cu{f=T#pv%8W3iJuV^fL z$PY)+(PAa!D$&`pdSQ{u^vVykZnNSjX9Z~vT5!-g)>bPjR#S;)S9P_8EC+0$=Q>p9 zfC!o!c}5kBEr?Td?rRq{x*GbijAVtTwH_W1$C57PnWhPI zfBiC{tifzgR_3lQa+Q^(tvb~5B?l5^G`+%I(QznWyeyFgoaR)LFv>;>pq|D`%!2yq zu$nuem|Pg%7R_`Vx2`YMChmV}`t%YItpe;uOKGY21Y6!+d)YfMdVO1a{?B*VH#x7H zRY!z1Q>`%B90n(c6g#~3P3N{)=E|ndo&mnncw+PT|5#DCrXoFQ*l9*|F_B1iS1fc> z(UgRh2Fjp7##58#5(M}kn;yBd31KB{yohF2USrWdKkQLX6I~XBeK$so%}@uF$uba8 z944w|7ALgS^}8txb6z7pFlfm$+Ec1gSf&mbj(ZNm!9J9}+OXJJhUzCq1oF6tJ}<+1j+qJ@utS5Zsm9i+=%{>OrcFz0K6k>OHKXC#_bFM zK8y+o=Y>@2f>n#of{>|NFfBpisd9GO z4JsV&v~u&CFp`%4VlvBtAz?y|cU4&yoiT!^1!sh0p$ZY*X?7D%dETyPcBC1N-BzO; z6e)d3fbjGI_97{I^y32>xv^Y7^LF4l?XJdV@d8K@*C)^ck=PZpYGsa?k`!_v^A`C@ zhE#>c_Mj+Fk%Mdi@0{}XrewVDQS!(D7zOC;3V}9qa~?}8bw)*E_m<$e)UwbFFaoQ= zY-bs#@w4!)Mleq3B{0}be6GQdE}~HC7ZZKp@KB+wuA6DEn+srT0nt}iWCcGTe}LHZ zQ?p`gdbQRI0vT4LP8FcisuK2R`P@^td>0x;P4m{ z0--m8wKIApjRUQ;$nfQHO9ae1)el}L;U-e!9j8DnfGU&HBtN$rmKg(?3;}@zAO>Fu z;(>sJoSsTYjd90tkQY+}JHmgRp_NT}bIQmyzMftA7N_b=9u0Xd^uo0u`OrJI^aeFK z0dj)#Bt3jd&;B;bj`Up?bnC(WYqvx4lwMo*1UE}kWaSvm-6&PI`uQ5~XMLU-r@Fp|W^tsfy+NpswZImEbqAONpQ?vF(k7V7*7$0D`?@u9 z*5Q=-W-?1cyQUz_FBzxrpdfQ};yVTwpbm9(M9i8;o`77+^ zY){{^U;8_seD?3X=P&)Ae(|^dp;tfg1J8?tzpCnKa5EfXJV|1~ixRTOog~-90Tq-F zRn0_1Hq|)X(nMs3>MKt*_F9`yp^S^ffKlE7J;_XvBoFR|Xgt%z7O<@|b&>R?S=6yA zh+3c-+Q=D8;iz5prj>igM=~7w=yk0F$CQs;hKz&3ssFtbZ;!6U@S`KX|HViMiombd zDNZ-!z)=9B*_z5+djLm!MSQ(Yh0jY4?r!3pTqA>E{>J4ys{3E!l_(g5Nwa7#DV0gY zwyqW(oocPtCz>;rJrYo>AW6njs>z-n+%Hae;jhUOkiNBVKj64Lp=iqLGg9aUykIgP zi3!Y}g%-n6NGvQzTef_bPI=KFQLySu+E@Nf|h3f|+Q( zI(Rp>A{ocfwiNsPd<000a#sP*9YhW%dgnz4pl#G@A3KjD&{V5XtXApz(#Xa3k1v4j zf=yuNEF#h{j&;)XGD0B>=wzZzt>u3u0YvrM_*>#?iuho#%EzFrR0J20>Sd za^3CT5jJQLKz-&ouqrB5QL2tv#A`t8CCnR)!W>%=K~t$l2I?-qPw;XmSag9$?n0qL z&2x8$(l6Ln6UELmA6@Fzpv27q#Hi)3NZl3+L@T@l9ZR?}|DcI_X^vWY-PTNol+C#yp6)kb0Y>KUO6sN4KIhls-vZw_?;wW)0zcD#YJ zD*9Zle~D2oncy*d9@DO#PGN#9qVSo=Lz;krrLNv(P6_4=yDl*^H%nJ5=&TMT^^4$- zfWlOTs*reQ1B_4JMk3ORvT2LxUJRTCx0zF=tt)bijek zI##Gur^CCUmB@$)!qSW$j=wQ3nq;DC0GMm$0m4bIFF2_I49NT_qD|x?x3{w2%I%x> z>@B<8`VJcvJpl=t#4&8-0_bl3b-apYVwZezY~Sa70h?15J=ol%)$2 z`87T?eq_P3&(^{#EH_=SXHEs1nGfQ2_d}-`#`cd-$B+5&e{uZ z)IBzE&$Zi18~6=ZHmDxU0|c1p;dt8C^#hS!aTE#lq{A~ zEn0HhX6myCaxfx7XG~d^%-ZYi&M~e?Zt1gCu_BHn!~`_lDTXMuq;OL#Fz<6c<6Bd%M`F2t0;w#gr*l))WK9c3=TFBz= ziyiO6arA3AGs=|`QkK#>Qx7q!3S~jWhHY6Qe~RfHvUV3AecdrZ^IrCq1TK{&`+{Nm!pt38c89T<$~!hhYN+k!ht>0 z3n|s;2_n`~2LXSnk=HK|)kHT@lgo`fdqckcOZM6??caWFf8+Jb%`=tD44HJ3(@FHC zdeW25Jh`4mHWoYg7un)PubBW+yNF$6_a|m*H<0l-`DWvjogdlhfu2}M_QxrTRbv57M6wZn; z@VR&Pa&Q-#ugUc#BGt*sJBEy-wSnu`HNJopu5&NK77S2wrh4s0S*h)m8V<{0SL?|I zlUK@9&=G20VmcC--NzQvYaTk@EQ&5{x#WVjl5cJ%4_eDEn(_GOU`Xy@sFwtfHGGJ_KG*W^ zKG#8#jlCW-!tGH_n}j*_?C}u%&AUlvK{-~qqO!Wbz`*d;=!?MpjAuYcx* z1c?^yJMAikm+mW!IrXRkXYw&CSR`>*h!!pthr5|)A%1mB#5}U0`>m1&O4iW@FX@jc zbuy%j)N|VwBZ3@HS51@}xvq|qsqRf)FJAh2*fz7bPD1@e4D8t+bGzZXCB0Pjy zeD?)6(k_a`r%UirZJ{U19$dY5h|+0BJmR9*?3EK z0INBMl6~f~DoR*b@Z)R6G;}c0DG>&dRnU+SnqUA76aZ{^+7FqlWJI?Mj{^E{mXJI7 zi9ZdK(&y(82!6tq>UPCa7%b8nKk}v2pfme0B;1mf>7Lc4S$keA6~>cp`lXTt!*vA^ zM*~G)Dt4?|ti~#Q#3yC&DFiL@o+Jgxi5P3wlFfFIUlv3K#7UyZeN!gJg^u4Q)1W!q z;$8D>lzA-DEDb^`MMa7iN52|uPU1bKo>>e^!?ewBMl>~=@r5TCOOykU+`|WlkYexA zW9G3_1rLOrE6va@v>1b&bXEn%W!plHsS@TO_SF~wYQZ1$T8pAmSB!uq>T~ghpd1+z z5Wi9IjHry~C3m1u7-elYtx8GA&tQL=YU&;bfSgnVZCR{56M;o8%Oi*TF|1AqFr25g zswqhXJp$bbW%0w!h%3@6|4Z1;-s~RpyeZQPZiOiLM&fGatx&kQieJUIm)CBiAy#i3 z5Cs3!L!|}gLRw4~S_*+!){8&Bb<4{eed6eVM!}#q9(|Nf(}xqg;Ij9#MwkhbKbT=|Oqst9d2 z1oWP;S_tgO0l`r5Ue}iJGJR|L1)FQ1=f5;riB*PG59>$WM;%pPTh$Ueaa%}p8`vZ^ zvqCkBGaUAh$vFC2o*eOVbV2!dRPTr!LVQAYQa3DC9;%{AAFuRrb&5c_6Szj>6l{T# zDD!EH$W=&l`PBJzJ$)Z2TxA;?I2^4dT%UWMECKwuMv%#QIMPK<&m3_N? z`{C=qBwzc5+h6?r?bkkcdG({`_kVhO_`>jZX1mBtRyBIa7ixXt6&Ad)GeCVj6w0Ds zbiWSZ+Dm4ULv$uzc2yTN0++>p8Oc6YgHkYJSyJ)2zsaOk@*LbmcoO-ZB?m1aXmLXE z(aXy~272fwQ;^Z-q1&r>?{Ey$;;U9&8`z^^y^ps(BqPoj<1F!A7Z<=)I%~#MRpuGVX6MYsX(%CD1;ITA1&l5rbf( zim`y@kNowRBST-jpK#^kf-4Ag^!1Q(nZ9W>upfon1zhiL>`GLzvU+nHs*f;4Bm@sa zc|Tcb2F&W3Aufp@Y_=Tel1o5-YX$S8f^9l0#ls)2+pDuZ%v!9${Umvw|Ra6xYNMEPiG8T2dF;YUk&E*BYG6L<|;!L79O5qu%av@Mn?SVzAiU6AQis1Cag6d!vLW ztgW`=J9=W&RKykr4=>62sSlX|T4ffEc5ixQ1MJg*C`d6rONZ|d@_Q6P#BpiFcODgs zTT!!gP4z@5!&u0zJ^@t?Z?wT4ay|q}=Lgv25U9j5Rw!-^-6x?4v$X}#nHQ-oEz!*D zqyt-^UGQZNb5pg-z=W=#^&*xf38lI4nZvGyQk$+EJ8C@hfi_L_ynWCa7hXmdBD}&>8>^UUM)@G9(b9d2AZlBthf7-tI zQ@g5u;FG5heCqW0B{7l9#cUV3i0!fst|O*V-xH)FTktEg9mDIWySoPLQ=pAZ_Y@w4 z*sYCoqh}C<16S5;iCcTJc2BzCg zx*m{EUge}A)^&uqETD$dPluegwnkS&CM-8Sr&u_+xg`wit1}AzC<&gHiM=c~1^7oO zXwjEd3E{eRVM?`*o8*~$-XH9IoDaOS47s>RjOmvWgf1N?qoYC?Ircb_f*peYP>1x3 zwSr1uhCJC% zx-}Y7n9S3wmmZJRp|^_ujckjN9LR^=8W)9{*k+0*C`)Zv87GLn%5J5i)^7DBcc@%S z(^jJ14BiaKnEwaIP|yIYhKsifNZdQY2#+{v=E}WW0#!r&X7`qVX%}8KxFi#AS9#EV z6<>C)b)u)x!xRG{J7$rvbAq+V0Th1)ZbSt;m8p2TkV8PX)e?ORPf3AVePy;Z*{@o7 z;kVN&s}80ew%LHXDC#zC!2#z1%N;E;H=x6+iYNI5q}|wT_Y9La!AO{gmP%c8fjG)f zGx{*|Oa(ZgMn=v?YvRT^Z1YtnN$b5U`w_EB$kSLcI;r6nSGrPf^6rje{yB!V5g;fl z5*88J+;oE-!Pd;J8s`&OxF3Dkv8^}}Sadh9!Tz@_x34ZZtf+f$zXxzrES?Cf`u-Z z_L}_ckL_RmyuJGI)5m^|K6#Ir$nCAjZsM!2dYX<;L?}WF+wl)GD@2Bj%!_R%CbE0x z=-`5Y6w=_4byV!ZfvK^H=7`=CTaD-Osvlr7Z>)-X+jMICMdTuSl85h-PyOc82fpj{ zGe36oaa&Z?@Kx3jujc}1*FRo{uZAHcrB(*}CDstFqis-s-B z+jaFCS#xX`G#vcUIIe>1)g8OPqHD$j zKFWMvCeRcP`zrOl@vF}(83IUE3S4RAAUtpbzU~*<9UNXqk!s81!Dr>+Q?+@@y|yx~ zv>REZsRF6g->oil%GJh%ek8l`4X^bJsl_ zg3{?A@1`;Y70c#x0?0qS(HS{x`n>Kb(vHRDq!8S}4jKmn*hIy z31SO=*LbEueB9_8D=O3i|6vQtg;ST0obx^WT7{7su;j3mH&kOil*q$bY7RTHKL27d zlK}alJ9JL+h}$|#+>zCrqj12#1~;U3K4Hg2(p6x~t!7E9x7c{Cm|9aisQ9ym(vXFnD!kaor(AG4o>Oh_tkeM5fwZ(Y9qP5Jb1+P>pERUgRZRwll0 z#Ye+SO>R4mtChOeyQiptRWqR1!0ON3aKz$h;TAhNj)w9rG*D{ zP$6&?&cIC{Cn8VYE5G)4J^bw7(VzSAo43AYAOH0aA3QI+B^&wHdWa>*Ity_)R%|2W zBK9HkEu9J^qR^_UDdwW&oG&QSn)?7<83Y%9v~Za`Q3ehLM2<%k+5UwWf< zu%|#Z2?WfJ!l59c>2332j{KDvEIl%H1ViC87|44@D1f7GugOghTFMB(+3Mbe=I#~T zH|A}m+)0MO`F!QTRstyvdJ%E_2l&W%0~Iizm%sEt%MDyxOIYealt00K2 zukCK}&k&c4rPfq6&7YY?*g-L8gE(<&E0Kl1tea?AJU8M1ZL25_C0Nayb11WEatTeU z+V6ofv*OZ&imI zDVC2i0$Frt%e%9W6|4N(dm=i&Xull2BP+3^;B~OVJ!9`kD{60l8P|lA1_<$*6-ZtI1Xn8gFSH;yR4KiJ%4y4Vt6zvS zLy-oz?E(-KPAg z+y>)vkzQkz3vdIj-VqODJdK3Gf=PkA$ z97*e5x+rdmk*O9u0Ll5tXe>?e{2fT?)MU~58vZ6t40$%uz>_shiPqY=BruTiN`Dca zjGX}#a+KvG>NcbFF5u*1#oED!%R2W?b=9bN^;!bQ{jNphgjLCXu^+t`WqfIEd`-}@ z;`R2B#OqXD!P6_{wcdy?Bf`oFzE%RX*P^RpMSdd>(^%4jht)(xs?#;!pAwx|SHdGw zN9lgouf3qH0fH zm!JNz%hRX!tA6P8@&{FHB!EZ|=)E*t4sPMGd;yLe2_vi`T+gV5v&O)jI<-3553WK< z7ZNSr-TgWT2;n+KTdHjwFP;!RbP9$ak}{f+oO5RL?W4lt^d)?QNY3kTmBjtM+LwQ3)*fmaA*`_h@U#G=`tM zlJpcl9n z3f(cdZa-_s7TdfDrAylx05H+ZZkGnLDAhKYkG@ngSzGi2-RS}=4#uO3C%)&L=e`#! zufWc!Hc4c^gETOiWA86Jc6{1CSH;0E#rZJh>sSZeXzQIyH9lIQ`(LYO&;Z^^W9pdC zu>~k--eM_$zw!j)JH;cff2evH0xJ9s+U4j=ZHIJU;`0H{uSXl|@t5V0JIVf$ApEfP zHPX3DTE%BaYqR$&&MVD0AWi!wAhr;GupmOEz6{^%G&!}_Be-qz>It|7 zbXrsjM_1TiOi*E*0T+GDMsY73FBv5Acs{U^w#qP5vG39(E8iWNf zkdIpG^05;1cC7L1lTHRc+G=qChzo+;wW5D6Wqc`^-yV(Lt&Dx)&X>jmFRuPFOJO7x z(Mmt79X+kNczKG4tAq_wwL$5x@%?=FN~Y4pDyHYBhOA;h(G@s?AVIWtfeR6ybK;S6 z-&iznDO7%?NkvuV<}LZzAGDYyJWQ+rgvmW+Gc({;n?KNm zbp@rbE(H&HKsG?Be})z;Wl%qP*&azeIH=s+sUas%mr5THUIG2#$q z!xtYMhNfKipA<)1TMkuNy-t*$tQH1daQ6qZmew7eO$RZacIb#6=@6}Kk^wdzcvg#) z0_ZDtc@P<|gK6cFt}}*(@z5wcP3WXDT)_H9e;fO?69TfV!R13+S7c_X$-R$qm9ecV zX-K#A*k)C8lgcVt7r{Si@ioWgwV-)G;$O^jQtH z7*QIRM@dBPY=I6aszj2t7xXXmgyvc8s{BQ~PrNpu*AWaMJ5jj11!URz>L}PPJ|GPT z+2r744dyt(Jh9EVU7(5#mxk5&r3b$oHHAM}lj(98%7F{!LQbWgv9}$poJ~eRFa@#Z zAZ*70$>=Cu2V0VsXu~bIC6W{vgr5}H6i zOFDQY6!e`4P%r6rLJ>@;)|hmt`cbAx4ju&GNin-)fb;-nb@2wfO)G&rzK>7^GPeq1 zsPjrK1lep>%73af)a)Z^?eGe7a|4iX)zMAlBA@-cH{bgD{$1aHdg)c4K5irPoan37 zIV8T-KHFzCf8jX(LwAYV1X>aqjxoptdS>doW9ILY+3-w`@Y*yfdKr1I4ex{6yN0ED zRtQ=9;67l3)61=V;M3bDfAynZ{?g?af8yryOjX5pwcSt?r#^2Egj2g+T?h|QMul3q z*TWaqm}5i8z=NX*cHdlQ87q|{N(D8}oIWbQh9HH1^G@>8>s)mPtH#?mBcZs)*wNb- zA_rP>_cWfa0Fp&Z0_J%Hci7Z~++(+?O5vL73JCkJ1mD-&`QW<;DOK0rU?RjFp@8rJ zQA>LDPNm|weYCLqMh@>dwt(&?n7N^@9XsRAYA zL%IEKc#RzMlS8A*LQo6SC;eHDEjr6oW34pyj<_due*Nm~t`u_WKv^qTlW@>c>+R@r zuRcGQSF5fy0F&XBPrL#E*>k_O@Sh|2tfx}k=c>LVJj$I-tx2!v5eW*e++69FmOB9s zn80kZGb`=bjAKg|?;u22rv~%7A|jylimQMH8xbN+M5zz zGCC$^8>u7BCGUB;+9kDTfk;^Cx0r<@sEmBoGg4AwF_nr{%NU*m3-GhYRIt-(6rH3&F% z@KmuZX+Y;!P%XyuUg}mG2CDp#HM3EuPQ{7L@krUpE9C=e(lkYOq~GdJ649M^=4^7CCbF<{^X9FCriM)u*?<`S6QB_3VZBonHC)d7lTMih(B2He-1*re?0qu&Hd`Mc~zgxt^ms zTg*bZQaI|HViI@Ud6>WaU24iyfFW^pu_1}2l7m;QPtD&A@u!+C)A~^J04@_3qmOex zdMF_$-xqLX%ju2ZYKu^-u)bmft`Ac{02L$tE<>VoRY@k5L_$OiXZVr1xq^q{5Fa+*~;D?^9z?~zx zLI>3|BMzIhuCp)Ne^`jD`wq)MdFiI^e7Qc90Ku5XIxxx)Ln6}>0`gc*7Zck=e4%!P z3xtVPgsFUm<4Ls8MX}0iGlgsA%&7{XTmWAl7}1o!y+>?u)McP`h%AxoY6gqWr%(ZZ zs6{CsJOEu}pK5&L%D+a)Hr@TvUPhwOnwIzWelHiqitb zswVl#ErP;y_|9w*&u4HQAiZ9F4-UE8NjCG~1%U%q3! zJr%pE#G*ZI_teLle)5KwQV!c`gf&JcS)O%Mk*yAVQA9;2nN*lVL$NAvmg^`!%09rQ z-vh)~S7ETFv{%iNCWJ%Q@_146J}w!-MLhhX^3~I>8nLTS@4SA6!kl##KFc+E{Fiar zuaw6lKf1!!RmFiJX<_bmCI4+zWGIZesS*})n1FSx$Hjq4x~WJme~*>$!=;Q`pD3$0 zD2DwNT-A}TCTWL)w#6^xK=grlHkZR13MuH`_>42@96Z%c-}<_I?#JJJkB?b;Co{G2;?!>YZu_-s6p-CyH#OPJbT_qK_wk>J z**KhgWVggjY&Xl=W*&j$VGe^fCkVYiN);lwX(J&Ks)Zm04qL=Tc9oNS?E4=)|Kfu$ z{r%fFzqxNGA1K9gS=BtIveMe}^VJ*c4Jk*Ju3k051nuZC%A#sf)tQA{1`WAeZQg-B zF-Js0OK#M#HM`J%aQbVFKE@*5JHmtIuAKWUMG1&N6SGtUajw?>(+Yi<>-M= zoO;)Q_PO|kjVSDCF^R3E`1Eb`jq6ca$5LN-G! z50G65%a1+u1mJ2U?@aY@;KB2(QZ`TtzYJ%A`A&AvA`sdhHFER-KeND^FT8Ty7ym4^ z?tW6t!ssudxkXb)EgP3uMb^zX;L2`BXE6@>V68~aLM$|D3};(%S`yxdyVAdAStEuZ z7I^El2^$91Udn0dGhkp_L#6u`gi_(gC?h0~crzfA95SoDuv;xLi7c-B8oEzo?cKI)B!Ga<4!tbwA1XY0*>@JhTwyEXWnVjDH7E@$ z0%zo=M<@6~$Y?(Tgthq|ZWpA*-bxN_-VUHyZm~z3VH13q}CtQF58S30z zgWWi;18dTyHp-}Fa7N0!{2|_llhNbrB8YqVcG`*%fKCF`tjs}<|Jd>LAtS{)8=eJEe903o~g1Oj z#I}^7fs5m@whS%>96Mid=jxuAg|Vz36iQ)~2PF=Y)DdXD8FN)O5T!0x_buU#76!Rw zSWIF)1!KKJT%SQ8e^HGzK=G$G0KXX(gI64oI>69&1?Rq@(CY5~7X zTAJ~uU1y8EuKAPN55YCGziPP+1=Kopk}H%^itlp^OJW0T*{lH0-7z91taIqfTxjb1 zYz?x??1L&Ah`=O*aRb=%*;=kcQ_JRbKn?B!fSv}LxsK1q_m;vLJ_it|;bs&r&=yQA zfwPW$U8%?;;d+@4h^s67L)0F=xMS)1%0z$_LG>x?VI#At7^;*J8C*OdST9YRnaFlW zSJ0=q^u}-T#L*Srs)~wieqshLDIZ98y}8jk&G)@}3hP>)xaZGnb#!3!(MjZ-k!oQ!9-j9M&3W44AH>9y} zM8297k4={2y2$<0qiiB~N7iOcx-#>%R_i0rJvtw*wBUX8*Sgg5&<$RL_*MUUBprN^ z{!AqjAM>N1v_mW{`4o}U1N+98?U%lI`^twOJb8KZA;Rhl+ffl(ltVq!{Ljmp3&H%q zef+oEZoAoTqH@~mrQ4?M=De?(2MPttGaqCswVsYGWA31Lv&-eO+hvaErgO3s97~{c zhXvPo9}p#X!D4fTG7;H!QM3}to)-P|xJGg^-y4#)RY9$Cxwwv8?QDU-q!ddKULD(ev?5Y^r~y(i=%ZWl zq0D3>A$c`7eB8g*2LBN*MXb8oGLN9P)2Y{0j!48nY68LNIo@T2K|z!tO71U#Yaxof zqv5!#P{4v0R{F4}O&Tx6adqUe?40(iBAF5cO_Kw@%FSXlc9qZdq6Vhc*`c>#(vlcz zVO7YiZ7^HJF!YHmUaxIO%F^1zeb4Kf-n1o_(m3vFEGS4!M&z^#l&uaFf;)DF^v;iH zo#h9EpuOcw!;Rshhu%!|VYRqZgJxM=8I-Ot4(%9Hcnmm0-QFQ$&J_h(C0ghsj^1!+ z;n#KHk5Nyj6FwlB((W}zrXblrp)5Pg<t7x`!1vU6?wtvJU*94 z%L^h?hyVcq07*naRIhi&7IbOn?tqToWpvi=uXJLTzaysVlq@UM)~`~pQmL#?_MR81 z=|ZQqzCE~%7!EA`oo6cg8q;+r?7i4bs-ViGtts3qWG*QLyg0Yj%i*$nz?#7*bk<%N zSu!x_z&!$3w;yRm8AoM;dnY)=-jx@rx$4NyR=F69R`d5)8**^i2W=Pn`b#~LlU@2M zQt@zBBGQ~)J|TpsQAcIShN~at54w0l<#jf9r{0?##2?;Z8BJ}y1nG z??9JjQJE^iJ@u8N?nFdsyrQHzppIVQWmQs>c7#Us4d09h7J(5qxGl2jCF#>Y%rc6% z)|iIbX75*GHIjKUtl>?aYhl`UkO8j$(ii>O+ z!+Uj8NWc+hhj^|4e`g9|=D5>nzc-y)>IBT81GEAw@E`u`S(Q732jqjEf|gcq4lS!n zJ4nJ-I+ZQ7q~7%SG>PgXDe4Sbe9b~>0_l}3RvP?A0y#RWDYHte*34i)2qp?zG}I%k z;qSmcnSBI+l{X@VFKZ_y%v(8X;0J5gLJS(PF5iyJNZ4${l)L8zdUNyR1rC;yLFvWd zTP=x9`8l0crBzrW#$J&Hp+!FEG_Ys-1;?Seu6*ou!sTlH!8_19)$GWy?#e*teMR^_ z1g=Jg-(2AUL;y(`UK(AOsx9{78rcP-3xqse2X}QV5Vr*K(#8DffVd{6Hqdq#CSAOz zRhft=R&R0(V#l-FfKQ5eoQml`V~>9Fbrk%pZ-!A>wAsg++YFWo z+{{GeY_<*pd_QazAOwQVNqP8{`@FD0{#;(5IpQ|-4I<-fA&X_Jtr*V_IHbkETUu)C zN`KYJ!BiKvQbyasFBCldg2l_6T^m}P44!w;9)&}Bu1NJxIkfTXHD-K(;X<#_Y&u9v zRk}9roh*q8G!B+cRiEQv?{342jx0HoVJeOK>4Fqoz0??-ynWIKw1ze#{a6^GCIJq6 z83ukWT|MK*({&QSxBAWIss2AXhFXq9uR=7^GJ>lsx65**C_ zZ4pDLTy<^{b-}v@RZtk2tsI0kdl1Ql!b0jTrL!>4o9ai=f1QlLM& z&mAX&UD*MJNar$MJn(b}8BlBbDxyn|Y4gFl8JYUICZ2P!2QgXrwDsjcB;mIj)yG#6 z6lc(|{@u#A96wt!zog^Nn|B8z!@;e_DLu-f8ILHS6F0-VE#%7c4AHIMbMLq`_$wHw zi&DBW4-pP^kkoMfB@V-CNF+KeZCsSv=n8W*h(;7I1&TdQTxP-g*m6b$o`9x$o&NZ$b^#ODIvD(zq>ZEWU6#@m0 zy9XUoUk9WBQ7fUueIE*n5mr6jTffHTV@iM-nU^jyuLR${CaY=+td9Pj1pOmi)Vz0{ z;T)9WQ#wjz)%;PC78Nb|Iv__6@_n^Hhs|Pm%PWh;xS4qPNu7wQE;sLGp=^fxxGTUq zuSv^kq}$m)+Q|BanZ4-D3FM@VqvvW%3%9wV%@%U!ysO5YIfj&@oFp(YNO%|-z>H^7 z$8@Mi9B^d${H_IIEvD7D$)XKl0QON-6!8VrTu1schj^_c>TMy*>69aN+(ku#TK;-r zcWu@fN-h<;o$M%;g4m$E=L67&r&rjFd>l&-G`t*m5&jEhR=gi~txmrAp2J07V{HPs zhr#pL=NH70Y21xy*;lD`$UA-+TpQg`oHd}rrSj%3D9e$OOu?(Q_#7h=&Et3&h)lOl zf9V&^c6rYSw{4sR|){iVP7 z?|$PO-xQsZrI~Eg@91_q>!zlURL^D?GZC|0%w(6#Ma0B*Z~o$twYKQyp$5k8t}j-~tY`!!(dx7M z#b^(}hD)jQ;o5i8*TD=Y2;#hV)S7hZe1#3rv(5!chLaFdrH`a)DSk!5CC2btKIV;h zPR&GLsWYWYvy26~jrcwa#x$4qJcwb>k2KfqsSvyBYR?o%BW#vv>I=x&T@Z~PwD9EH zQ8Y)(D^DWB>m-P|?INYHXJwQLIRcD$rD?f71LQ>WQu(q&as`0_-i*a=clC6fF$0nKS)Pm-UI?lU`7fG&RrxH3&URCsE9jjbnfB$HI*(D7(Hni0RuHlkX5Cv zWMsj0{l#OFiAQW@R+L|25K?vrJ?%i-oj_Vv{^l3vNs3$O#6dJZo2tp4sn@|&Rj zXE*P9*UNwPul@DE^cVi(o6q#Y;}`b*BBqy-DKZngh+Sq5$aa<6lk8%bi=ONvmuGMM z$RGYgzw5XEw%eP_?S6B)z1-g3zWL_2Kli!M{pgSW&CmY)=lvMzJhMj zF!-x$Ecd%Co)cV$0J+x7;@ud~O0@9{atd1xz3UQAXYq^;_ordJGL(*js0_dM2l-Zpp8yxIeElClO5p$OKP@1p>#t#V=15Ss zvr3sL)|!1VqZYFO6`^_of+6LZ3V*uU$Q^=Ph_zMcjd{7mp^FUd;^uz-#XIPQew-~9W(|H2o(Xv6yT01yJE^O6`Q{tn`lo;P@qhos+u#27 z(;xbXpMUMojJi>bG2=*ph$eH&I;cj67=(?dZcVw48>x{p4+%DGf$_R-|NRiB5XL}# z`b4^E*>u5=*_m@@H}5-RU2lC`152eSix=*M3d=-1)WrIdH!Is=cUA}38W)6cipM0i zX*I*EW#dpZ=jAIa8zQUjOV7(8U-WECm?)3kPKq8yhPV{^MpU_8gOM)0zafNdC>h_5 zw5)?|Ia8ojfksj*Vw695nvzxMiTBWsfWywl#-e24w2tx( zQslyp45nbRx2_dk8Q`^$##C5Yxk=N^Mwk^qC9TCk19DOgl~@^bRj+zWWW~|jS~#?3 zx}F`Bu8K|V@kYUx0tX=%GT^hERkcjhQliQys?zPPqL*rQa=-LoT52gPYLL@vRvOful`@N}+CpI)<9u0N;s7gqa(afW6>tIxzCEKOTawz+Xpq+# zooH6Ohb%CBvQLxVP96=YYBQ|JQmHwT2KJ{%IgLw&ne^{~#}f;7lG6r6#fVk1`&}GD zZS4Gaq0xl)#L#56A7UVYU|<)n%QZ_=ZQX?Sg_R*PN*BG!az;|AHy}8Q(O~CAg#jq3 zp@gV<3BIdJ(|Rbd)m^)P1mr15)#u?~1;ZEjSZQ7m9vP_On9skWMhz$SzM}@(-Vk|} zq#(B&k>1HjjIUe4TnnXt&vIxBVR`4P4t)L^%)p!312sq0p1p!K6|jT?OT+lzz78i& zR39P6pwx{e{b0z5-ks*Sf*_f(@r_aOK#gCaHZwV8(v=>BKzn!5>I!W-GIWa&BcPDp zNVHgM6mNxTP1<(;PY92wbG(F5I=>kJMx*h+{?or)c+mw*>(>9#o8R(^|M`j|wr=gZ zfY~}yjBz%bg%I{UaQ|#=+I1a3Bm#m!gwzLuj$#*MM>GN)Ax?-!1RY}6jXNBVLqM4I zqtQ5YA$Q*QT{l{{ZrvGYJp6C}_HSSMV=sw)|MqwO{WVvAM~KF!5&>yVmP#9cnEIw} zP)Sb(42OA%9cIt$u-hR&a!?FfWvd$##LCebAVY(RY!y4NXj0MvRtu&UDTnot!5|A> z^^m}gO9T8vUh@M$RhjY$KeML-8#Oe?=hJd+Mc@XLTnQ>mAfS&R6l6UdVepD%n3Q4~ zT_2?Z9KNt{b_d)lLmc!4y*-=LZ_UbWk8TLsQrSW2@PXwPzk|2N!s4OoJTsmSU$)I| zzG>8e3&q#g09XxFirNvCsR0n*a4(jSc*)M|nN%-3end5G4;l^7TRYXKQB z?#;^;q^A3sXOTvrFwdo%yipJhvuCFcb0veB5LmTC%2x$`?Q006jKv|9bGykob-cE2 z*rEU4)wU58TufS1Z*Jd+PAmGxYW-|$QaRiN41n-4WN%DbvwscL0);9|R~82cyRE28 zmE*dmvK+RibVTLzrdS=Gn^eK1PrQ?-QPgC?ur&y1=9M}Kl=)fMpp91vgDWtSr%3@9 zc>yGT1Je{ar^Ad_wVYNVUBs(dBQowXIIQ@Z+b0XrZOo%hUqf<^>0}nIZ|$ttyA4S~ zUb7~Fz0?GyKj-@|R;(Cg4`c4!6UVkFM+Oy@1ZXRMqE)54f;86jDr{Jmt`r+eo1uAD2-U&b!hC|Nc3X(IwX!@YYwjEJu5RlIlmuu+_|EWVFi4O#ET<%W}FD*Qi@E zj0OOEcJy9t;^WkcS-Qy?ASRvxlp;I*EG{fgZoTE!FMjcSAbRPG zU;6B4KX*JHFE1~L5RBb}zV87bQlvPWjK`CO@u-hPfha@>KtK=?ItH2$yAH<`r!jPE z%LiiYPJHl*2(UJrrmu(r0L^9qM1<>?*8j@?`sFLHxbl)qF8R{=UwZh%&Iln)r_&e- zk&qCO5HVI1^z)s{aH9Xi1ZkWNfO!+!(q`Ou53L=H3mbz`taC5Qy|b1s-6#Xakg8&2 zYOhCTx^_H?)lJP*sv2$YPC?XE%}@VXDq1gFmEWG?U5|2NfEaTSbfjWb%C7 z8eP&$OcFBK8qW*u&q3W%Xbz@m`N{%19C&gn)U0i@4Ir&vc8}%>+ZUfhDpp61*hw&# zw%(Sn^rBJb43Z&~FF%YVc5K{q&n)h>m{41=!vrN^?JzkzQzUMv ztCI_OW8e&TG3aRHkCdBoy*B`7c$|s#wd4JY^(H z-&KcAw4IECumz)J1Pg-e%2Ng&Z5`>qX|=uLshl5OBQaP|S+xjNDR2tlFkbBDm%0}D zZF-31P*%riScDyg8*EYZRmeqYZ_Lv+^RhZ8F-e@Q$WYmyDp=6}Pq~~uhR=aS?pCNr z+DbhfWlpz;frNUMmTh$^QzxIL$Op7R8ZcfAG-j&Ol{xO^J8mr!Su8!oP%a}D(wP-m zHZR0IV=)h7u1p=V7!V^X=rw&vTpo>&xSbUg>qG$f=MMSK(jDUN9jpG6Mpi zBw~;tQSd@A_TS`HWj8&hP5WQtMNaGiKu#zv41gEug0~8X(yGnofD8arRPhklIi$R6 zjnv%sp`^2dM=;M!)z4~KweS{L*KM|KUHL@ovHEPF*yMU-UTLWsQB-b-TGT7mYuzb` z{x3@=Cqmyi(VKgby)$_d=}&6Q44i|hR(Lwhaw@V5e>!8;;HvNGB6k*JvciYcpixrI zjkw5dC+{qYL-UM+l;tU|Lf-k^&~xDVL*>i(g#fU&3gb~2EtXoLqE!2rF^|e_NX#Ar zFkg}S(P(t(H@cXs_zV@}h_Ou^-YK)O0bX^w$0w59fec$)}Y&Ltu!yfUB zr$6K3i!c7lSHAj^m%e2CQAfs@&YVV)6U2wd)~hHJfy*RD4OC#Ggb;DvI+#vry2eOo zm2$tZ8%XYyFs=_^meYZwj1hEEFlr7cXjO_9Ah&eN4 zxaQF44`@7HzE-ENFo*zF3a~&n&wmc#d$f921wNp?m0r!=f7RX`_tF^hM&mU+jes2& z8$xa{9A`w1gr$o&+6S~%+%Qv%jc;g=M(OY6u&CORFg$?vuVW7jbkTV=>ZR{jDNNUe ze@A3p%GT_Q$Oha&tWb1Q#JM`x<1j zp&o2ASx3omktM|;$eAYjniY_>Tj4Lc<*O2u+>V^<+8Ti7V}}F!o+<_{q_ccip-JVz z*QVcEcaUT3q`QckY(V73L43J-9)ablm&9!qcWa(VY)xwCT!9iS9Cs&Q+7?4|<0~8O#sB$9-wew{lf|BV7{E*KRDQENT zi7)7Z0eiNWv29+p*NhG|rG(}RkJo0#?&^kNw2vECL59Y9Xy#+I&!m6RbXB3!^3~|b zlhV~Qf?C*n*E}~W7R7VE2}n~u49V=H_PxUZb);xXdH1A<zCS^yP0 zb#e@is0ijA;&S7sqkzUcw}Q=si{^=O(p^zjaRmpf7@Mm(6F|nbo&YGwZGFNa1TX&e zsUfya$KnV;I^(RAXn9gdCPlp!mn-of!?z>^4;aWhX=Ye#_{^#?BO(TlnkuYN1U&)( zxO?Z_2M-=xzhUF{9os_}@>x6pAt$;cB83oQj1M~DgnxYd-~H8J{`Ixj-8dd~9pY#- z=_ZrWXw-FG*QK+AFjAb&VvK!^v*mpUpK#9Oe&*+2v-OCrYirXG0)`N}4gvC3FMxhF z1z3F8BTnDCb;}Jm+^}!&-t9*nNs_ebHl(z9qwqKiJ;4Oc69Yv360CS$5wHUYUF>Jn zPeHUs_HRnfT7gj}YU%|+X6IDCU^oC21fz#^N zJ~^lg7;^*8CYzV&9UR|;(G~+HxahH-bc>C#v!8i-uf<1H>8Cj@pz4XDFW<61;KDBW zf-py8N!gY%YQ2zUt})b`^hrkVIRlD%Oq33R8;q*sqIUxX)eIpG6DbdP&#l}KVjoG# zdc17VY0z~u;$)r_d0P&cV)T)hUhAlk-k0oV6?|@9_xy--h@b3wtnsu(Y~+o=h2#uO zLh*`~xJ}Yx_Cr=IiM5m2u(86+##QhXs8F#>)7dvctfK2>@OfZ5ge~b&FbrF%hf)Hh zn0q7c>m>`Tz@wpA;TIWlP^nKU#X^R5=)iC?%Dq%G7o%sLVK97Wur5_>5twkPNZHw_ z%~5X9Q;R&k8Y;+uZxs+zs6xw)SP?&|hPS+G?j?596hcB01#Yw^)3WfI3s!PA%b{A- zOy~<0OVa>JABZMuS`jm}r2K&j4R_vFh81U6^N;+V^c|T|mNa&gI#CCz5g7u9+99r5 zK%Kf$HK{y;ik)*}%0LCr-c;EDTb>4BZCLBTovoqDx6*l{C{U+nrO1dy_UJJ8%*du05w+pOyxLEoJ9z;BsUlD&X#;I4|C$sAPX?($wb}3`Sy(bp79OVS zzIl;37G{1)Xbf%9OH=NP;tf`-hAWqmqOufP6>Wb!q9t*Sa;E*Ne9_H73x~X#D2SbD zg2V;>8VL?M^gmA!t;;lDJyw#e4N8c|um&ldAB`hk}dM2njWb#^5M`oADUPyU{3&#zLi(OpkyVvZhU-p8n8N|Kv~pcy)C(AclYdA)jR6Cma%X zi!p9kT3T3KAfnNz10cj8{DT+*#B@CMXfzI8w|wxxbT$J3ig~fu8(-bLSa?juNax4M zK8*{zSSq3YJ4*@RfR0wd26sfc7W#sf=+|ETKV`!SyLoR^?NehiiIWeAg zLenG~9XZH4m9X3P57S`h+Q_MV%wYo!47A$P+RZwaT~xX<@WzkHPel9a!|x@R6D(g0 zSdhkO+Q@%W4g6`j;;n^BUbzMnlQx1=@)m4aYTdVVdjULid;VdgN%|-02=D%JKaIAn z5moar#QZGlz;f>_ehqQ^VzVlIn9(tgjBUVBv0$kLziY36cJQjXu~C&8zr5Gc45Qa= z9#WGSQ&N04{tzNS<$diB)b7S$3j^?aVbndPLw z`|<0SB%`2Zby$Z;-Ztok7E~c>)o=jUTd5g}w$jPnxZ^Pp27HaS8{Hntc{U~e!>(u^ zy(VttD-OV~L?a&r%Eb?95RHm1pAP~A4S}rn+F*afa_BIXmBe~L8k(DP%A^Tbs~zLy zFpzmQ8nFGw_Lv2-5(qM%FEh`Jb3xGlFJPj9k5sJ}CR+`A{*4tVq%G&)dbpaSg4$5i zwt*VTtM*TtWvs3!tV()DMOpI<-E<29%(5H0=e8k@s>+!&PGPyADX$$F1`$hDQM`5+ z+>YH$MCpcHQ9(ibXdeMknC>De-ja_T_m~&Xc3Mztt759?DC@}s+Lx>sluyjEZ_6Vh zev_*k=4Gu%;usOwQ@2cH|u&9=Dno9t4uFS_- zigUh*Ta{oa5E}NlN)^cycrT^ZnoWvDmnGvzd@dDNRN+6xS-CUr33QPCdD) zIdL2Z$yEoamH7v&oyH2O-d#ftBO*` zvpkj-KL-M>K0#DnVax|*`z#bt!(I%i^o>ku0^;*+yx<5yIp@-fxvECA8rR!|qGy&? z5GnQ5oQ&4HicD-mkVAe_+f68*pnN{63e1^ys*pDocOv6dmT{xkT5hf&%s9yS1iXQzAIb+!I9MrDecjH9?7TJw1G5*^`10|JzxC&!a83_Z1JQPka z{)TEQ-g=hWW%KHFk6PE3{U`Th?w+&Ofp`WL5r*zCRVqpS;H7! zHWVDR1LhaQEJ&4uWrnh7F*6nbIG-lLGZfzUhOYz?>d%kWVj=^pFDi5@p(>X4gq0Gf zpc^VVc*AL|RDpbRzyTt0U|f7pwWZrcsqE8)x3t7&Y69ktS;W!6>Sy$SosBo|6lc+r z{L0T-D`l){2`Cbd1{MaeJv_XxzaWBFuuOvjd_#q*>1-LjK?LJus}!4{s^zMJyYx=U z!R8=8HX-T!HCAfmJ(IR)z-CS2QRC5&Fiq7ICkr`iX5lx0O3yw6%Z(+^J{w9Q6P1b7 z&?QW8lbR@FfT9kpE)g~3^?5}w@rH~5o550c!G^LCNK7G+sH_clSdS#TyrvUrJ4Zw>@d97E7{PT!adhN_7uvg)5I$DYXl0SI*kGgTK6tU%c;x3++Z7jb zdaY9-&8AWz;yLKRv;+(@NWKFR0TCvVB$D}mSXrwl1^|$@XN|hi zXw(U1Lb|$OchV*5%~GBYZ-Hk9^B#ruk|p{xHx4)Jr+wc8A=HOID?*uh=2P4;Av)^S zQtXV9Orx5O8v?gMl;JXB$8%;sj-?SK-wKp~YB{Xl6@dCBDFV={#hh6+mi26#8@>j*XcjJ@Z8t>4(M4E}G8MEYwmwx$rYCGZ*Wc1WQ?EJY2+&|W zFAEh98&J@VbXn44kwT&tda7>f-isGH$e5#(2Bt#@f-z8{o=s&Wav|^Bjn&%9W(~{J z=#U4)l<}QfYtUe*Luh?yj&MVf9Gc5nsXDxf|5_=t^eLpA+}QxEK%Hq+wJSZloI%K> zsI{vKN=Ce?E_C6IAp#T$^tl6qHkUSxIWJcoj7=O;`~Xitu)kV#t{+52h&(qbjuj7p zydrew-ugwRypRcVFLy9NvgI5L|6mb7477SrI6WZ?2Z)U)*L&^tA5;WKp3 zyrCx<0|V+C^=hVMszZ7RbximWS0NKjsDW`mbaO>P%lrc=NLryVRAfkBHpoym?7pX2 zZo{MpdT8CS>!OM6&h1(A%0J5~s{XZ1qB_F3Wj2jKz6-|K@EnHoBnmVXTjSw6HQll$ z<)RGCF*zu2ov08Q<><)JP~}vXVedd&Wt)arGlbJ1Zv5>W9^FW4{IDFnxegEdN92D9 zcjRJb0|3`&#xqZOP=sMBljg0JXFsz>RAzbcFp9CUVEfCMeX&CA)Ad@_=WH&8p=4}y zibr3HKqYap>yn`802-0y<7u3Y5r#3%BFA+sP)2G>I?GTNW8+wfENiv5IncsmxDn$%$pn(%o~~%6tleBzK~UcS-n9tO5OMr ziZA~#L~{Hqwio@wqy(JdHQ(BBxly%NsD@JR>tWKDwKO9jKswO}sw+7bZkCLN#3}T- zQ8KF4r2km)vZD|wDh{$2jtF?ySlv?tHKA+caRF?L1ro5N<3_Sm-us4%rs_2t@$%2o zT13Vf)QnCh|JDVw^C3VvQ(`Lfx-&uT`+Jj`{S33`N-!mFI2!iMY5=ITz0u7jGhqg; zo_l*krqMpM?@QX1gi6}9IEP(Zawf|4=P)&5;dca}5Q$mHiZd`7t4#-nudA^~K~0b? zQf14}Q<@2y7!D||!juqawt2wh>@VAI8hTwNb_*r7^)3d)`w5NmBE^FhHSSn&UZL99 z#*1Of1tkt;qVqDesuK{vA6(%^qChXd@LI{M*(w18ZSz?q3cVvDmOTth5y`GBL>9=j z!b^AR+2;KMWRQ0KmT{JPget_!iD>0kVxyE)E{IfJ2;!K#h(g(^@Tv5kIZRBDmzwks zYD;ZQ6Aiooi7TvYeMX(I;A<|G=%M25;dz&Gsn(F8tf|SvT{|s<=y{>8#nG^&1<6&G zO6=zuHSDivw5Ez9DmBU++Nd-csa3wzM8k~{dl5spmlf)A zJXBs@25_%?88T|=UbH@Up&qmm6Q?r7DiqDu)KG%+ogE9+)nr>fl-0Kqgjn;PBcNm9 z91%$l6;Xsp5JtGL4gmmWu)GKM?xMAW{cJ{|!^yg^ers6T1h|+Rn61TrN+AFSgpi}h z))*F>+e-l2$SCPBFJd`!q0l!S1le!y+*K_g7KB9e@qV8)d*wD8TU0F=y1%iM>t-&6 zwGDtFV8E^$g%F^1nWgU>58^-szXowc@}qj)!yWcP=0652K#oRv`&q)0SHEh@Fziv( z!Z44V6oERfC4=egUfCDgY)@~1joveX)KUegxuBANie{v9vcH**)^gU$irTMGM?yMd z*Ch+2wMK0N0c>EUjAdP~uz=NyM^QbVT#+n%3Z6xR#7U?NyUW;bs^8?hI)lnMqeE!h z2UyUwp6)re$(FuxERd_&?6;z4Zu*e=SpXMZAcN=if&-YxSmnCuQlwN2v0k42^B)3n z7KBHD;~WUpTV+S{w4`HAiXueSdq^cgSiB`xvXGb2P@Xc1Mj)u5$v;S}0LMslxV~?~RY6?PT#+)5&*#nPi_)N0 zc3zBOJ+$6Lfo7vv(p%u0S8c?sR%4DOI|jB_LBz-b7|L2(=OzR5i*tn1%qa2xr}cU z5K)YX1fxml5FWU#zv-$2_uR5}&#h~_?^)ZsYr3{Po6RD2I9UuEkC<%UHrf85h2u_K zIPnon+fE(eO?Jr!kEC#%-3qIAVWDKrDK z^n~8S znZENVW=cT0@ve5@{27i;PQ8_I35nU>)L}#0x`csrs{t!7jZ8@%g<#q+p`>ScD{Ow( z0or^u$LeN-BM8HjHR}z>K-V1EZ!4zN0>3CFR5})uZCRlN{b37I20jg*^_UScQLEWF zt8M_7j+~lQS>r@<2e?!(jINJ;V>J}@1Wee>J=Bg$sgjvA7uRqh3Jf-sRs#tjg3XG@ zIf!TqNa=ECxHEIovf13F#~c^>$kC;6O}!A8;!&sRi$!njgDefY>xgw_30H34rWZK` zMb~(KEWwu#iI#Iq*I>z1nz}6m?zFS#F@5X|d)1A=#iEg=caEKHogh{kF3Ckjfr;yi z46>_0hRq!-*l1dNI**M%WC>&g$sjgZTNkhnA6sxlX2!OFq50Q!Z$Ou_yn$Vz%|UcB zn;~=iBDqxXCYV{PnijGOtiZBNd1rw%)67bbnFj??Y6Xa@m?(YMp zSGMtXoE2ubqS^L8Tc}S{7($8d8LM15;Z_~k zA=k_!Yr8p4Ud#i%oMBb6T2eXuu4O4sE3Y)M!?(vMxx5rrt2=z_fYVA{1hyL;?#8 z-O@qvt~R$&Q@usAfy=HYxlU`PSOH}(alaeG;&;S0ny5`Cv~VAKFn}h_dD+5&=x_*T zx-Fy#t>y8c*+uc9k%}sot=jMy6_9(cZ9LhzW{ZD-vJM#<6px&FP$)iDjzk2phsgp0 z;7wN@yzFxiTyx>_eYekM%NY9zfdC)`%!kH7fA4L}L@U_gXiP^OJv#ZZ>mKvu&8IzK z(|8@sRw4j~5nu{LATOELM+sZoY+Z_$V_<6Ot>sK-9ZWNJ;*k}G*#9IziT&fyS^Z88 z6lc_<{BOcE`RE**%cUmM+w*h<u&i|utxMNb*xEkxvDm~zIS-kH zg|FPYxy4St|fBFk>2J8j6DcpVR`YfN>0-%`L-o<46ZT^-|8xC&Nbs|_ZY=)!aS%;V(^ zL(N0rh?y%_eh5KD#M;h6IaO#$X9cPKS*143ckgTf8BrO|!CvsR*cH=D#dx52X)!Zn z^t*J(o44A@%zC1UPD2HABesg!BSyRzSl^SYmmy?kwaWXDhue9^6eJLM$-_pV=CGgN zR4=fi0%7PQT{xC+YF2x-N#|7-+HEX#psnn~I{*drb5(xR>xO}GuhbxxWGpUM-wNYA z8Vp)_&p1{P1_*9N!s=yHfDFZmRY3z7)?f%=qLr2+?()$e5Y8&xN<%-+=_%N<5K0Ec z&@#)F-^X0Gp1UM_?^`Spu~t|J2u2DjoYl@D&ncvFXB11^xIy#XDzDqIeCD9`It73v z>}=a$Np@Ot1STGdfW&hl0npEn2l+h%+2%uL26Sr%8RujIN)2Z;rWCa}wNLkTf?}4a z4F;KGOUiLyg=X}Y7R%(?Mz75``4{3YH*dZW<9J!}B!+r{D2jX4V#yhUxd*y!}IO$;zJoWiUJ@VP>siWB{bz`^w z4p>^=YHxO7g@tp@rZY>B>TOzM$fe01*Yy*7MJp04nAbf7z`~yZ)v-QZ7M-*YYoJ22k)83l-`AT;B!Q+s9aHPqmsbC{>$7 zoWA3~a$GfeOqNhlvAj5TQ8o)%_|Vox zmpxRlQsw{|3}>B+UJ;zl05E$>q<7!4!=9Mw)Q!t%!t{EcHz5>tBZYe=Hjs8FS*TV)cKC&|`P@oLy&n{J3j$0Us; zHUDWRFd6)X2rXGDJa!??vL zvWEpU)g|1m;G;4J$Q-{RgjQ4K@@QW+aii4Nu;evhD;^2RbNebHmcBw=dR|jaxYS^i z@5MFi=`X@jDTC}16q>w(5p5mIY{fVaVOnVuOHU7vxgTh)Fb@j5$D$tXX8J`vn!`g; zjaIfoZm1obnCOL#I2)A%0OZ1)e!;rf_yp<%q_(Ujvko^&Wdb0GGr&L-?7sM}`_Fyv zJ@?(XhBO*a7AKp=p+iIH3Py#7@8Jmk`#jL91lZx^2LfPt6kI-Od&UlefOas+kt8tek~lv zmViuAbElqd8h4&20wQ6W^PzxJes65xI^>QyTkJ4HKkiZuZxjN129#$r%v6M6iRvH# znboQf#(ns5DkumXlB`NP6KM*RL**Syv|HIT99ERPQ7tqOBWYv7?$T@)AN@geD2w!> z6HD)N+X^H^bxFu7%LY1gm86g-S-~^kT1TlmzM;^q6BZn4%^u_kUSvgyTF`W3g!jYqClHYVF&%NF0fc6*LjsP=8*oWlHd*_YY~~>W zv<5IK2i4o8-3?p8$|K^e2q(OTNK8jSG@M+G&_GA528+Jk@)3cOxEM+;>L+VeXCex@ za9i_KUcS3&D0kQ4zysrlR`zx<9^3LlyhQ+mvk8V7*&?S&6SI({m1c7)CeFjG{o)+s zQiytaAv+5NwVAnm8|jEWRdoB2}!q2LHBj?~*j4h~5A+*ceS<8^kLp zRZVWL>E*SuIqYjD9jki5K}LN*2h`$Kgca-3#91P64bsVoqO@+9 zxok>@MgZXc)p!*6sGZ=xR4S_EOBuoV9!opgF{{0kDFm9MZe#L*7ZAlDL zxwp8iWF^^BPwrHLxE=zyAhl~S$3cr7$(F?^O?LpGU&GN7EZ^OI`0wsK_d|Em>SV+E zg~?(Eh(IyML^*+oV0x`a?%qQjDq6-A8gH-z$)>&5tN^i7Qv*-7MUS|Igdyqe-YR%I0u z-%MPbCU%F4{@4_ua_!qcr=qo_{nE@bfb0cp;s&5!!%|*T@5bSk{)*DWW}Fq-f6*U_ zbQ;W*Cfg9@H;zC~cc@pyB1z<0GYdO~S+OvvT6 z>uji=?Q4EhNRXLtNofb2R5kR}1R@~ebpAS7%}OLKWgxn7_7Jo{AECg4(O;krAta*85yOy!_?S_5^n+B&xbYD zPT*F^8McajR|qo#79Stb$^QrqEwj3ff2B&5j|>slw34h!YtTC_Ghta(I-$s~ z48aREgRW=R(1Kg(tSZvxp<+i+xJGvZ~9N=d7} z*VJ-M1`$MAfv`ABpGoJsRCgQkbx)j=0+!8;q*udQ!DQ7bm2bom$#|+(D~>xdfedpZ zkF=be9kn@zA;}L;EqS^)U@`DmYn|e-9cVLZ#D`Oi3s8^fk5QHkqyMeC-&3EdwQyvB$j^<=a!822vqC$^ zQWpfH;;F~4Q5th*b~5bMcPlK(s16mD%+>;|c+6HS0`*gvtjGJWkMH^8TP{6s?<6d3 z+BE7qiZeg}2$;V3j));8^N4t{#3Esg2t5#Cj1&MC7rTCnw_UaR&Ntoq@;%3$_2P{) zpnjD?he&{7?vV_Kz7pHEcHgi?=S+8f19^6k=vy zoN1MLN8&F^^StdO2Y^fCEu`X=0A+muf~40xffyXjxqGHGk}$N4jnqZp)E$JBc4IiU zG-_-YMWl>OX{n}Zrq(ktwbGRmK!;iDi9SaF%FIlXZEs_PmE2Hqm-k4dc2YBpG#P-n z!hoVv>t-6klG8|)WkYim=2f3IT!Zh;)hWvks~+I2857|%y8v8difxniy(L(pDRm;N z@|#jZkP^(7@yeh8&?L2Kx9O{tJopw41q;OvLR)(|CNUOKG#ZGz#)1{C9na!#BCb%4 zY1t_eAO`(mYLVjC*ioeqqVIvc}-2TSBM%xnD_V=Zjs*gDn9 z(8z)b6A?ACk^$i&7B?u5W-UPF5T(f@lq;Ljv4|%xV0yHBw z5aw#w3T@IfU0vw70=ZU&RznW81=m`x&x7o_O{3D9A1QhVi$a2xO;oD^D8WhdbK?D9 zfJ70->tOeH>7BoM-8a6vymj-I^-CerlzKo2njxFKHB5{`lxGj=KcpBb28c)z0DvOI z9tdIUmi71VTzUJOZ~5uj@n^qeGj$Lnbpgz5x!hS=pJ>y(8XKLIgU}aY&d!rE@}nZ- zf;uqJQt8I;LQ~j*Xh&4_jW|SLCzB#130BGjllwAujS4+Gf0Nh1CfwFVWbnj90z|~B zX|o2Q@k#X7h=UPc84<|~ekkb=vhIQ&pQbpK$m%MP@gef7VlsrHvl$?0jBjBa^oPiy zx0S&DjiZ7Xv_y6?T3u^erbQ_BC{_4!4|#gcPskK9*b*7@<#**EFd8DlM#S?Pn9hHUr;nbwW#(#w6t)NHTc*X< zcalSVUD!@cJ!Da`rn=FJX#sdE4}m$0!R&kh(}c8SDZ=mAKvYswXY3Nw%E+Tt4KfrQ z{4o)3hC*gU-U8>b5hj96Wa~w;2TwK=y5T?;c29p6Ks&m+@f=W${)Vz*5Mwc*Bwbba z3k|_cyQNjv9WGpr$SV}CY&Jm&iE_$mVKoc{$wGjRNDgCTWt=Qn_O&sHc5dcyRb%EU zOeziTXb;z#(nid%?)v}~@#coh5XX?>Dez*|C!L#~n>4WYzUqrPUG#&f8q%F0APDk( zq3R1gQ=Deydv(Y3sH9PLx?FR@gUT^YIbeZTjq+4L9wTf4~{~QGYF{bQ`6;&LFV&a^A zGisuSg3`utLubnk-UIZp)dCW;J;OLee25ycO0>G zacN;TTk9}Th)Ez3BPNZCAXn^gE)(@am%{hyJqkFz1!BUA3gH< z>-&x%n#k%Q*(4VeZRJQOyQ>s!)vc=FFk5N_paAYz15zK3v zaHu-QxG=VD_ap&SqU*fBLTULLrP%;>g^g>(OZw6ylJiQ))H6Pnx*A%>90|dii~(^o zT@wOt(>2&aVrF#Gt=Vr%6eO?#WpGtfiS#G(c{r5V!5MzD#8+MX4M50xrZANR%J#DC zAkHp21Oz}CGox(nH+>Tobkby?(GlQy9A=E8;G*tkaOAl87`)* ziJWBD!dhP8H}57G-EQP3dnODOub-pIvJi#Cbw#ioCWb*+oDIPcz$*dM9-!)ZJ{^IF zk&U4H5((MP(ZuKU9Op76rRlU#%W$xM;nAOCQ;ION$EtkR)S&!IL^bnd!pKFQsjgXj zcSY~TmLXFB)io%w@Igikr@bp^M=47yzJ;1-ddsJQSid zF`67YkD-Scaw$g*v6y1fu&mY4jHW$pL{Kl$DP}^XGD4-iAXKN8mF?**wfU@oxD*v$ ztVv1A#`OUwD&0Gx2si(%Q%RX9A8dnP? zYGkyWuN7m2O3y>e17eM2SQ8sd1qN6?IEs;}V_+si#Bj;Nxi2c_5=yQ&IFR$3atjp` zH*AJsZ;wQ6myGKpf?WKySuF*qyuee8c8QIEIJ8>e9+h2iUZS0zNLeE}`Bl~U0CgSA z^$9x8nwc?lwepj)wuYwNry4BiIz6&>z}ULZx5SRXx9vz~13(sq7-0h8v;T1SrRVM5apdO3bzv>8bO8{EkRWi( zFV7E%sDs!A#BKxx*hPW>F~$yPMj=uJ%6=dL1%RGrt8upN$jLoBm;db#ZvEw>PkQh< zi!&nZi9(7jkXf%Q1*+caHUD{fSV_Dp=0b;M`st)ppSJ8jGhOyWT~L}03Xg(oBHL{< zZM8@`;0u0hr%y8C&9`OqhZjH6Fcq4~UzA3VL7`k8b*Uj)th z<@7vqedysfP+-D9=NGlGm&QdsLP_qcH%;(B=PdDt3h}mqnazf7o!2zumnbSes>4mf zH?^0SOJ_`NWZnr#dRoL|5p`pihOO(A1a_hXHDmI@b=;sOGmc5GuW}@;B5+!_;Y!E= zXf|7WVVHdRvMV$rvf!-8%NeFF(HU}+gt1s>nY64eEqZGK>ed@(DbCW! zV3VpnvP4vPSr?@0B8HW1v|fur$6sc0TLmd0jjHmp)v={6DWB*sG)C3o0NNZC@5{FB zOA~ZfYGy=>coTx_o7Ju;_M)$nS~V{G-g1fUaxf2w!S&_*@!%fz(%SQHZ6qz=n`4qp zM2N&R$U{mJm|9?FW0a!+VaM&iaWN9u2wV?d&h}D9rGUYj(Bh@6&MCjw{ni?!ByA2V z^BbPo&Le~>hLV@kRqWgyDo0f@sP28YQpLmEwph86p%QW~tx}#`F-vVIhNBR$TxqTd z7guYexq;Bk?W;1(8(HmU{VS<}c|Eq+l~Z44PD=m~5O4&shw(UE{;@shedL}EOBgdrV)fb2RWyBkyi-@dnAnT?F_G@c7)M~rQ=Y7U~b z9nRS>BT%T6;Of@Y7+64R6l0*%Qp`eDuz}RV=J=s^5bg6OS%xl2HOhlo>h267seYE+bU$^-phzJq@yBAoa4)l4~&CC%s>;Bog1Vp%5p6Vs_uy z(FdXkhyYPKP04gFxdIU+Vk9cDx@frYS}M zjJao4@qFNN5Q>&#pzf7jofD}7V0q+;SLQFbJ4=e`5aiy7(a359HSUR7W{W+tHYDf& znD!xslntY!0mJfKmN(h;M=vEE6T`@}lFh zOche~sttNZ0+T^w%#bZh7BD-s0FDuL>UPM>62Lhy+%JIP@4}#KVeQY4L6Xm6DT_c0 z8zDSJkFN^QK6&k_OdqcSp$EM))y2tn9I z7}!F{u{@=2(3jvTRuEyH+H2SFZwN8SlsWSj>f6Dp0ATZgz|zVCOr}h`!Q^la_B9WA zVv#72QXmQ^1gL(*wfrNijm$hVzcQXyQqkremsEzczK%x#i&tF7W~dn4L`yfMG1t$Q zK{PS?($20L{m<2<6?9Kb1)JXIhom)9r6iJn-K<@ev8Iz>wbZRxNM!o60BMtq7a64q z#u$2(u>^~wB(9nh`@{3CxTsdOJWLF}j6#t-DOFmENzENmq^=A1Ts`~5KioP!INH2< z5}^mcfCw0KU=lMM@@xkIy3oaaYx_=r;)atRw*J8K+TJ}YiyPqno8ZRF*KpRs2%ygl zkfs9xLyU1{jkX`X;eyZaJ>v=cp7zSkxCqpXxbT8Gk=GV;$b*Uw-&c!BM(w!JIM^T% zQ^~P9vxBovP~Vv*==D z-UT{bN+)vNr|B{R2u7C(Dc;84f7Ee2%+ zjVX?WEtjoR=gcPkvf-BMFa3+e7ifMNe-TwzNH8OlsDMwmKcqnt$Be@{HdW9Lq39Mf z%gTcyS~opSgJ3hJYCqB}$~Mq#BrJM+&6W&}^I)wGRp6IhTcc>xs>y2$!*)3)&Uhjg z!X*JpFc5e(V8YCx!+V<4)K z!7@=HR$LiWlao7OX_Re2j=zEsHt)z1wdLW)a)7hy&!1I)bdS zn^x7kWIGX^2{zao3fbtZ?MV=qydRG-uksg`Hmjz@i|8E#)mf=lO&^DHNn$l9CRad^ zGrGfB_8MbBz^`__`w8P0 z{_2k7A2yy%VRfaSEQD`=dF95dZ<+0(g@q9SLZkqZ2r#Al#L=wpN2Af=;`sCLz5iiP zSUTa%$y(p5UDWk>ig8}3A_V<@guQQNwRl!h_Ah+alG0>Nun*rv%3^@btD*{#3){4Q zGjITv&a>B`-^`9GR*Tx->!k-LnW&k44+$MaISGp`i~uLj5u3KcwbA=bx6GPO0H+rLK+-+UjujrF6#fay4XD~EgskXzP1fuNu=d?TZm-Iw zX7%8F9#Pbmja@4`UWEd21SyPh+MaEzC-sA+f1i(HL9-ZOyaw%1l@*u)H(nmPKU@^0 zj6C6r`EATV81QXzxr>@dnc*&7^EBhhi5tj7@lkrlDu|?|!L`U|s%0-LV31H4u;w%- zDDWr}_rPgZcGi;lCLMla(yt&hyXX=1b`1V za)q7Ws6}CpHG&kl0w!U$dW9+{drt)th^h$$e&I&*#+vnDL`Eh*q%DG0i~3uq{mn$A z3N!cCMSqHS4tzti1S>z5SxZB&WeS_IRXYW~j)O6kGfa|zA{4R?1wsJq8E6AVN`D|( zL!-DAR12gX@K(RJisL_qXSkwup^s<9GL^%wynFQW~|l<7y>=RjH!cw)Oxm_wW-eI0;u;j5mCzIwBZ#)Y3Uj5VikT{Iaw?aH$+gI+d!mrI z4?+xALeo(z(TG5iH$=>!o8Udy$4kC+-#CoNUFf0DM9I^s^uOp8AfgbuSzKK`xblN9 zIqvvJjsa-Y;b^e~fW@tmIzq&*13;i25Fn=DZNvyTg0R}hZCe*_yYawPpWnaZ)Ggf- z#vTG9Vv763Lc7k1dd$~EW|=e>7vfiOUVd-8E^1R%(EgSpVPAdqDhYAq8rAkgSQ>FU`2Gm8vbAm6;08oqA+)jC_ZG9D_vg^KsBrMWt1tJLpW#Y~a$rS+9Zg8t8 z8*H&Yg-&*yA`b@B>gM8D%Lw=<4SP_VJ~cF2yfk!L)FuZua4Q6+225HaR^qx>vsMcH z$G&4*hsXm+b^9J=PFCV^FAj^W?VX}i;8Yk;!h%Ll7jfjjHR<)33KXK}Z4g>DfNBr0 zr44{pFHO^DD%juD7`Cc*nfoF0F+&TEo7x0IrMwe8Lq4l*963!agiHm?qF~A!b}bMS z;ANfDWp@ZC4yeJt^biY*)b zb4&a;4VnfX&5dnJeYxZlfE>2sl(IFW5{J*ijsR5gOh&=5v~Df5>$+r(SaS|pf@*01 zglLbO!IF>MnFfvstsxd6^-C+wGP@kn{H<0D#ixSQ?^i zH~#Wql_McgT2a)(9x&%6wEL{DC@jE>Ej7VqP=tLFYEEf0cy_TfJxmmq;5mu9jx~&t zh+9CbSkvPeVFAM>pS}0~o&A>am|#YcfC2$gnte$xS;}SxuZ{Ze#s-K zGGvtuh(cgxn;r#Aeqf_#Lj0WBAX~}oTj?Tdk`RMdm^A@TvrdYY&t_C^&SfJ$HXsOQ zOzUi!!WqiR7W1RUFAUQ8eaFNRtS-LkG)lA#()Fd3J^eNpz1yv5_4o0>BW2zlb6SB2oU;WpjvG(zSQi zCOM@p>&qZZHb}eWYzY#7|dJDW^IkIp3bU?Hiy0V}{SGgnlEmR#18?R<8ct}Kf`Nh6LOJhxp zhVsaeUiuIP%1jV|$D9lC8)AWxC0wNokMNvq>gqCpS%?7K5HNiho)AQhqtR&z81l`u zZe)`0#toqbGuHP8W@U9*A`o^3`Rf|>mZA8?c0|IZ5ee)GNsy0_6Ic`bnLf!VZ~=Cn zA04SRzW3mvm89a+y8chBN)g>1%6Dv(C}!n=0`-ftQD%ys-L7 z11duLtp(pPS;#h&s->ZF0)iBnhS^NXjDzQ|21tHwadOK}kd2ndi)n~Z7zLx+1snk)_g9jVlv2tJQJ>R9*t|NFGQ*0szj!FtKWDj3Jw$LTQD-DrHb+ zT3AUR%H}NDTtxd;HzWVSlDlCJp{!=d`Y8i6+>8hZvzUhW(FsFU$4@4TMSN@a-E*+C z9JV=52cdb_Se|7s%L<`Tv64-*o~bT{5j7r-O1&yRUI|Hlk1?-Ga5ZcKEjf!@kFk|| zuB;6fvO~3FBg?`@y%kOCv@Nrs!O#w=ux7TCShZkUCI^mf5V@U5p~GEw#S8!Iu6_+B zOQT4UC>@O)(qt(gO^Sgc5JDsvhwg#B2R3gRKl7)L+PDo@S7LJBLl+RC>pFyhNmn63 zAVSO@Jg55y!WiSG&7+%cT)FwO zea%-A$0D{;3vX+vvv<+{9y1zlDY3IpB$^-(i}=D&_E7pAP~3$KT50Q{feWg%Dc`Dp zUg$JuzKmW#4jU18duZfXx;IXdw-%Wovc3nP3nYM$qd2;_nfr>mZ0Ul2+6^!kP;RTq zt=IBhjbuHvGCDQK*01cS)vxBHeL(`69V`R8_%A$2+=pQ}+ZF&?W^CbwEzGkkMStHK5!Rvv8ySPij`IlGc#+a zB1Wi*X$~?+yV9T=Xo$%TmMp-AAGv`-jqo4i`&g&tuvKxTHJq=ZhMg4syn8Jz0wP)& z%v2#(kY#3U+4(S-MnhTGXAWKiN}x)qGmDpYwD?27YHH zfW%qRK5ALJ9O!W#3HO#Ahl21{%rQ4SOiW9PWR;pht^!rjKtga8a;t91(6$*%FGwvv zTLB<$_Ap*Gx})N*RE=8AwMx#?wJzuK9=0qxXsU$87X$j5q zVhea~tE%qEC`?dNT7#3U@anWfHrLj%C)uJSK5=Huc|*YPqaRuOSrXERCG>S$Xv>4K z6!KIXDPS>c3Ul0W<;u&Ig%Q$)WQ{0nmIm0VSuO1wNCR{BBWvh+2P2F z$iLUUjUlQrLde16d@wM`B1sO@B-6|zpxh^8BqBzD7!V@CWFdU>n|toOVP&zK|S}^s~+^AM=eAk2rwQ45X1;wM*$IHOy^0Y&IuxhyahO~ zV@K#CjmL-pw|@J;W1qiavN=pwvkc{GhqZL)iEF|ig1aoFs8lmGH7*Gvw*)b1L}1;7 ztni1uqr&1k#w77I@)bqz0VnI`?LU^c1L`7I*_&k^yXbcq*CBU}2|gwSBJ7E^=S4LlUAl|R$W8`5~n$J01cwM3W6U4sG3Q&BTKoyDmV=74Q8B7Y#kF~ z=xE_CGp7f`H074RYKCTNGxO8Xt9Hf1%pS6dkp!Td-I1~@%}aZlTwcMA1b`6|s<}kw zR`*qR&@NmtgEyxlA?LHFUaZD#uqYTGlVW6VO001*Xd1%PyH(zf7yuowi+ zWu3m0jh6FKWps%j>I*#-D8EJx3i1{=qczn~GCJ915ucn;GFCy!lHC#GA2BZM z;;=jmvC-n>FRreEhvA96Sx3_)qsl1R?K|Lx%PY?eVp*L*qL``BfVupds9Jz>*S@$V zXiD2f1JjO8h2{o%8xh|b4XmsHk#)b2CottbtJ`~J9a;39XW-lK$gwCv%I9?oS1S)G z#G!sosJrB!a;?P;WoB75%7s0$m(7@$E*zl9$F%qk*l$w^hPr_HuW;ZeDc19A4EBkT zwvnolfTUPUMbrSkges$~F`Cu^8ZEd`SsK6$oMBG0KR7YE`5sd_7$!Q|>emmEn*)#= zq=u3~aiI8#6y`#O!goCyQ4{OKH<^4zXd&rLGImp&fd*acK!(HaP?ws^LJgv1?wK)l zpe6wI#TS-QgbsZ`Tu`5yfY3Q8z8rlK3u2R^- zOk)0A)97lIkq0mz5O>P69#(AqGFC~lX$H@MYFrWnYIbSeOjM_(28`^lMmzNmLr{(z zM7xoXCAwTzhg$;&k_zGiobxXkPRZxSL+dqV=^8=f2i&i_fCvPk z?_p^?zJJ%QQy#MEoab-u#yDN00O7y`w6KxV7o<}%KN4r!QqGe_l&uE{ND!dwXx)ae z^Ny9ZHCnK`lJ$)l{=+ait;^)X6oe@W=u%Ux)g0Jx(MZ-P*n<;~i)uy3EnOB|iN?JH zV~7{)4RJ+6(EO-D2!Thg?$v?gf+Psadu+?oGvj!;!Ds`aanI7Ix32+zfZVO@oKHP8 z)`~nD4FPP6lYl&E!G;xl{n=_NlM5QFnzpq2JEw?sDN3CwZ12sU46Tr&1XU)DU@8!Y z%BW%=DxLY7qKCL-jHoiu{?=|JFkv@c4@^JPxcT|9lV#S-`n*AEvgeU{eQy9_jxV%= zV^w=0?@R}%+!4R{-<1Dx|L1pC7@(Q}s zD6-kq1uZgLf@C;ZqZ()i_*S7cc%(mAJ~4L{t5qsAk+$3z z1FYP{hhuA6&i0fwnNWU#L`tTD9H3(?P1S0wMJ;fBkHR5jyb*gQH=i9&sc`-ywLoij z=-WK3Is$3|0Mu@xi>#I}8$+(-^@XzlTksCgFY=|FzOFi!!p^Sc8x%)8mucc(w>$u5 zP4Yqwx~k@`2*@Yw9X6<)PMQqd22wvgf*%5rcc{LU+1wS%PE&uQ@nY6FI< zdQs?8=gHV+YJslxZq=K&!dUljjG$p51lofGVgUP@o zJ~6HnWntsJ;9|!Q!P7>4%5KkOV2us2<`yjpp=IV<`00)Jo@D6kwpe8kr_i$2t!)8i zn9IKEt0G9D2&^}TJ|@}D*A*gB(Nfc##u7Zg0otH0hNTt>^J|_NCRY-$GQTbE#E`)# zm9d?Pp#N){VbRp(VB^%d>Xc6FM4)E=M#WSEqsnLR5P+<#2gWol(CZR-wPPRbww7XL z8h~WUKVcLIU<@>cdvBR_FdBCwptOC9nI{4yzmJdzV-FpU4lXaR99Vt&D~{iK{Ag_@ zj@RS2K0m$p_LZ|=u(V-oN5ncZ4~Xs;&W=!0`62H#Tfc5}-`&%dUH!%#qx{7WAC0Wz z1z$gtt ziwNM~+E+uA$$P;{L)iRjWdo9G$@?Eo^#EBIQ>sO#P!T_7zdaQQC?)5(GXzFJbLpV9 z5^d|Hk^DmjDg8wX7&PZW{;wz8syBqBAtRZ<;$h+))Rt1UX=ya?79lGSNKgZxI8<*B zY;<5+&W-VhpmeSkV_q`lM}Zx-$m-W9Mgc27iZMb?26CZyu>{uMihpZ>mv3GS*aCa7 z6f!6jKwWBbgTq~bVN9`-%q=;1rP)%(uvk{i@WmKHNH>TWJuU!z(6gaXYJ`viCz*0L zHiZ_XA+W%fCJxP8(+9AcZ45o1I+RP>Hi=DL0j@~5NO-L6bZpdq?iO}&ac#zlS*^RCbQvhv0IJfxH6BSv^3BFkDB3h4WeYuuwD%c zH27{e7TzmYntwn_fVn)$4{Tkc)xy#wEWg76}^d6&VL* zC)^H0?JZ_en0&3G5UYbiT?13qS4i3PhF;2%jZrooRR%@J-AkhfzIE7WS;j!;d8iQz zudBB~W>p9^Q@Wo%eau>+qOi~@yAV}VjYCQh{Ma_QFR*@~s!%OO#v-wfg$xa{382Ao zbf!c`P*BeKxTdnPL$-QiBCpBBzE@Nk9C!WvHS&&P0bS!dWpy&&@iK5C072xfNURt` z5LC&VOACgdhSy>QncBP~Of#ne%&Z_r^^{9dHc^GaOaiph%!d2W7Y=Q~Hi{K|ff2)E zeV_tar~OJe8wyFSg+8*!<)lCJG?)e3ytHNHoXUo+v!5U-bzUhz*mxy4RS_TsuQDvv z>aeqm`LAYR7cJJaEpMm_u+8FLep>(vwYqnJr9awgR%g03t)Wu63#|ASD`J+F%$0$q z#st&0sD!B+@~;x%{a8~$R>Mhy0LgfHH>N62wlgXp^I^d?4_W)lJ9RbnLu^St9*+Q^ z>u}}Xc>mqg@pv>Eha4)ktpET8gh&X4VHBo!?7a60&)WWoXRQxoT3d9h&ZMt${{IA01ElDvP+AjT@S48x_x%k=?fV0N5nw46hPi2)%<)_ zrLPZjI}EVp@<)G2jK>d!+_g2|oIX5n*ULr#0M*PMcsEBwDF6RNlqA0T3XYNKnePKuZH(J5kbL8LlVwUt3(4 zh0QhN6MX6f2#M0x4MOv>(M+v%s3APWHZeyog>Cq%ZP1*)ArEY^7?<%cj9pe(oT`?a zsDa09mWep9hdJ)x)~sWj&(8YfmF{jatxn=iCwpYYmU|MDZ=O<{%t+PcB=RSoh`-k)vHv?6Afm1KU8bC_zVQ?n}E2GlDG$MFb_2wh7x_+^y& z@HIu&gJu&YSc)5nn`s$3b8QLy%KsupsdAv$o((K=jB&wwa=n8Gv_ zp-|taMY6@@?Xm^g+X_|)CgodTcz# zym8B!WUHc^>@2!Vtm)ndR(IdKHVR=hPUDS{C{Q3u$1?-~fIt!Yh0*AN`*&^LiqHP} z9hhwClbZ zLaE#kg|rY_oCSzh3V9@0f4^m zV~l-_Komld!_g_mK1M|Bx(>{E37nW?nIwA2=HU|4#yEV)y%+G2usx&?I|^_j)55B` zPI@8IVGJDd3(7D=!|VC3TrDxCBGVJtJ7DHvNOZ5K=l?mWbJCVNL>cXI{>gBolr8DJM3Jfrm)>E zd`$ic4+Z-V`oddG_g0BfyOi$oqC%PkWfAlc7Ks@0RZ-Ev7BKBx@)9b)g@y*msPwu) z%eTel3cG1!MFU=V^|T#emczOHERPj$dSC!_3R%3j>2#RFYue&$_Sb##Knp z4gjc&iCB`+CdmRq=J52zF{=%T|7F+Cs{;WJYnCX%@=dpkN&la$Q4(ZwKVC|8YbnQ{6IRWw(z%90jaqMYc0L@LN4g~RQ9b%7xQXA;Op&>>Bi|_0xG1fVkxsM7<7@_`5{wEH&~%(!xI7b@e0BBksHQ@T zE{D3f4mP|$B|_;L2}4XI=ISZ)md%mm?ITslG+S^SILGdEMR;C`}fWE?w%q} zLKleAH{scNNcm3)kq~h-qqX~Y?fZ#ed+0+Rw=QC&5w6??|M9N7_8x={n@1Rkh$vzR zND(MPoH1fV5Yj%goYuixGpXxP;`qY#ELtY_-fDl4(d_~4g@&kcA`6l4B zXnDEasTG$V#%0BVbjE1p4vhlwE1j8_*mub4y*``N|3&~Bb=`P8PAm>)G$~Tw_kG`| zqOG_9S_BUV(>h>BL?MKQg$370xm;0NPd{~J+$&VuEMZ)Dn+j5C896?$tsk5H;xK>I z5#FH!05F-10mkV*#t8Y#f&y#$t3v1-a?IgcM(nz7JRbA&7$XtUc$`kLhuLg40DS$( z>NM7dg~6qgsty4QUugjNLdF#R4pPW*s9Z=KvowoflVNpZOGOR9qc7}UFwZb1Dvgjn zEw7NN=b`WbY4UA)robdul`1>O=G1@cS;0mv#XWMorOAfEIxauyFdVkBZ_$X=Q9Of} z@3598amrkSrfA{Bi5ywDwAl(Qo99x$JJe-2x`ic*N6>M%R^=LR;P}6Yd^)*X{u!lRl`38tJM_gVk|XQ?S%odNE-WA z<533UE5^&Ng>epR*Aj2^gIC@@a4AKG zU7?~2RF|Z9RRndV94PX2%`$QIOp@$=A3J(TEYtn6HDNN;437Jx3 zr#f+@zxIA=m7%X21dyjK~$Q*%6dNT>kj1nP&ODj-x)E!it zBUaA`*0j1P}-iBP1cTKB4aENYe>RSqUiHEQt)K*u2AzYgp0; zmK#}8Zx*Mfu-kq;YYb)FG=U`FZ>C4AB&_;r<#Q+&)>hO0>7hvH4N?8FGUdjfQ?*w# zq00#f=2&0hY2y;4tf9o`&ap-(DgNt!R2Xy>#B>5nwc3Ds5Fvw=c`WJE5W?!(+W!6f zBT)z;E#xGvHy)2SZQ7Iyr_g{Usm))LNAsf2rWU&n`QN_p@4Wl22kyIn+qNT)KKhu^ zXjB&VYcG&CWCqt)k~ZULB~|8HH!k8fNdf3r{*t43Y7ckiAX zZ@Q`P`x760;abk`51?gmGVoLmXzK`pxL-Sc8&d4O1Q~XPLY=v$1B)IZVF;yfyGKaI3aqE083{&snBE{(n#heyjj=qC zoIV2ZPJu(GfmcPJJojXSBX-khY!cZd?wvGdm2?Cul}L}J^uxxM41gEujlA(5SklA( zQorSzl+j;06(Rz+7px^H{=8-dj#wcWav=`2k}4T=ePhxY3!TIh&12OjDC3?<$Kzsi z^p$$H$efFe1(tCr4vsc?eYKGg^mTlA@JdA}EM)r<8|PV5LzPlmh5$*lwEo-h5=$zS za%gZDb>$xU_R2w)APE_>tWns6m~F5CP>CkKwORBn6|wKP-C*Mh6+Gd(07n@GN1sV6 z6%Dn^%Js5Zr7Um!b*zf(a|+M}2m}Ek0ziO}wnmX`dcuX#WdFf^{lV!Ae(|BlK4c;G zadBgJ`vt3?d(ZB$x@qHhxeF8kkwWqr<=r#qC zLln&3mDts!!b~HgUaAg^R)Iu~0egS=msQrPvsD~xpMZ*2R)0~XNHJ~KnoPP2zJ9?6 zKlq`8%LgY5ixm6B=ON&_b?Z((`J^X5beUqyzo!{7!(o28zg+KC3zx>)GjyPh^?%nVF$A9|t zXFvOWkAM8n{@l+z=s^!&Tblu3*ZICAdN@qpS{hoHB^wElg&y-eL(toOr&47%Vm9t# zv|EvDh0PahLFoosLPBznh#`d30Y8-KYH80{?4>2C$)5t3`MpZ zwHhaXd@{$eOnYd`3K9c-HC7Ae;#DL=DHvGa6Klt^z);2F4O62yRLk*Ct2#RHl#zkY z4r^3wed?3^-+JZ97%$_1urVxZ&+(LN<7f*~@z!pDs9-i=H2&mP@|d%GUUWgs7gz!S zFvuRMVrH{p+f`O+A|b$2S(LS#nXmm1ts(YT3F2dci?hmxxs2KpnOxhItDO1 zruJsNsd$4MwF1YZhy$$!1%wb1h;RjE-dqvH;Teqh3?{G`p8m;j9uh7iCTOM5SK>Gbb`g9k6W^s-AXx%ASm#9=kP4+8Xhl2gGO8{&J(KLxAaWYHxJpkNx z>#dhweDVGR2X4RhHUQ{j-}k-5#e|Gv)uh)*Zc;DNrM~ZD>;d4Odv;#%^{;*V+gCsQ z^oOra*8t$6OD_GJzkb^-x7~8_1z$h$qzC`>Pyftp)+1sFh(wwU!ep`dT09vkJ2nU{ z2B#u$UT{X9)=N2Qk-W9YnGN)1=W9V55CFM7R3;SFRQHbqNr^ zSJFto;(sf&7NELKQ>{fmZcybTikd=jbY9S_|glK@3g zN~E5k(*hOxrN+XI!@+pc10!AZ~+xmuBIhU%tCD$^|fc1;ZhWX6#h#>k`1QN?u z+g#qzEj42Rchjc1S&|l=Vvnf_7LY$;+Sxa77n z24rwlRfk|9uEt~Fs@_R;C)SD89N8@6qAA7zv5Cuqk9@?FO&OD})DZwjl`=rvSj{oY z2#eZQOtHrnPq7Z0`#l2ZcLkw z903CFu4t=7V3doWb_M=$!a%kG0VLanY(onQH5nIqob)87q3DkVo2Q<93e+HuNrDCm zF$dYk47{3(&nFZE9f4vjZ5e72f|7@|02l$Lf->eD60tFbmH{z*2yC2)WTpl7%r8Wv z96-BJC@So%8p#u+2C3~Pm&Nt#jyP)D{rBE~`soin=GfyXLLX-%G?(`N1^K|<02_YDWwGfFyOiki(S`k*tnP` z)v1wmR!vCXxF$*=lAOt^I!%3BG>ob@G1mB_&)I46l?_(`YS1Y`Nb(NRMjri|Sk}LZ z8F*y!_XYDe)OkXXqH?poPjSi7XuPv(o)xTn>TM5kH!G7xH#FmZObjU-Ln0tBahg!6(CKyDdw-jNBty;VH-I;3dAqr z(Lu<*wdiV<91otQxim%f<{xgnmTs#yn^E;5BvgdzjKxWv+9(^vm?D)5Ph#4Xr*_v} zcYW$ppSt6&JAdFQ-~atT@cqE5Ge!~y0g2-SNJOl#x5iG&#ts6I1L2*(YYf&;AY_xB zO(CfrZ8fI zkS^MC7#F9xL3PfzaBI1|VgjBhgF977nyYE{tj~c(IX$})z}jTr zSizI-$hLx(Z5Km@(lnje#+NV1SOo;hmnruRxk1UDV$r)8pSooMl-MYNgp-vv)rXo3 zrf0q@q(8?a)dgL{igfY-{gK)ryU!Oc?_zzb>y9l7zrC2TCwf zU|T8gC4NNwBRi>V$%*M=@ilE()qo{!u%K{#)i2OdsTuiqh z9h!d|YK%KnM9je1rlbJNYju@4l6x-J5$r6N^B+>D0wvmI6RUO-b5v?XRq3X!<4xPf z_uNXc2kZbS8I@#{DF%!f_v~H%(Vu(Ju@775A@)RZ4Y!@vz49-1U<}h9_U_%f>3AA1 zLI8*nx1Tcl>EAmYr{Vp7e%p7xvK%H8!~jGgg?|VDA^HE<;mRy-+%Va+eI%r9GDdTy zx{h(oxtf_T=26UxJ!L6GlrMwWcdj`-iOE-w`XxRcDM;q5N}zC{N?39jmpS3E`sdKq z7D7TuVF()qVdc+1^clHkvQi2xW|=T#K~l+a6jui3SRk^q(*iMlk|!N8RqA7}Jk8~V z3jp%MM=C!|F(jluuC1-U=!Gx**`NLS&~>Y;E6dBvx88QkJKpim&wciDci(;IyWaJ# zbIv~Jga;kJva-U#ULc~rk3^cmgOUICtWbShNv%c?V zv*~m?o6Se||LHtT1=F`yV@Kbz5bJQ|N}fP>73$!$C+K_2_r+S=M|)<>d^ z8=;@YewGJhG#UfI zsXBYFPEDFA9VElX&IKQ$F>KZwfE9jxYhvFx3+Yhtg87#_iN?d%@YIo$t$Y_jbWE=5 zfkE-YeB`VEy8!|W;xZS-+9hth!p0aR{|o@!w;QSkp~RsaUin>n#$3X!+^~DmCc@CG zM&Vj2HD@@>8C!uoXvPVVc?=5Dqz}ks+2pnX9HUw)1m85j2O})`shcWjF(A=@=1Oao zqF%rjn>FRJe3$SWm;`8@G}w~W0fURjt?r2OhkYcXKRCz* zG|C*c9`K5zBCm;A6M>5XsYHl2B<|T7I>Em=b(Shn3^(%=%AAC9ps?6&W)F+V$_15| z_+S~~Ekm+gUAcUYl&LSmq^~R|c?&8Kr&sEfDpm_a_1TT9t=W2$2ES(+iwG|G^fEYpIi9+EH z4I5=`40tF9VFsGB9hRq+1@eN+9GyhIeDTU`P$vLL^U!R6Tu(;4Ngk=v=69G?yj#_D zZijBua?3y{eWEi!P63D=Po&Un3T|2CN9L0}z=cCGXcp77nRsm?l|JH#Bab`&I3SwM zW<+$x8D|``*$y-7``K&;fL*{s zHW6{%x^?;lXl-@P0u#Cb0BN=?wF2U3VbX;T0Q%Td1RzS^H6M+}<8ijX)9EzrlbTE> z2rx_Exd#}9Zqx+;n9XJ}_LIpZ@D`Lrmb5{0G9HH<*GO2*(rHN&U+F@SW2$4+*arZN z$778zr2L((!||Ai*4EYlU}3Tl7BhX;rqjOfyU+zpxe1gKf?|y0(P(jDA>p#NwnjwL z=`_VD`dI|X;gr#6v~JxxBAWGmK$Oj8Se>UsL_m~|78)%qEG;d~`nbHj94Yp3*7K38 zi6D!M>t6m7|MTTP@jr!Ux~}W{9s`cMN!MlgBSC^HguE9fbwNO*(P-2yh@PL#W_{nM zoEgZ0KZvpKy3mal0-wK|ilOYNLO{UrXaq2dv7gOmA$0P=xI`Rag^2(dLi)zp+S*zl z`|)@@>XaoUNS#PQkRxtg2n!1n5sT?;*3V`f!ecv<)6QUVaf0TwV2M$tpyaxxbw?a^ z#QhK4KN*k5qY(fgLhOmCPo0j(UDyVMHtsN0jCcz32~XV8hWt#W;%Y-M`DjUd?6 z+~S+s$u>GyqF9Y8z$jo&&NJUTpU@&!`znU%#Jm=L*5g}DMr${r_-zX!l zqJyb!#@#Cea}qt5SC7OI9J2G}aVaYsg`=zmMoB2;v~7!gNkY%r=-x>>qA4T8;?QWSY*F!~ z+LEF)a^=a4760U(9WCs6F@9bH=JXw7JnG49LwbpKqZl~>OD`&JpgLh-WSjHNTdh$^ zpBMz4UMN~Q5)YUc9tc49zob%WdwHp%is(<#70b!6@?m>ne%6;74iFYBbf7y^zwQF6 z*I7q3#tq!ysNHCo3-^F&XFF{*FL05zojPhUj8k4M5WN0#-BBLB7Q8J=4}jWG=iF#| zg`s9fci9y-F=56MJ3LC#)oPwRH40}Wb4#g#EjTgxD6FjWKszm8U!<{4+cZg=L=b1x zE#i)o7iPG3ZEdoE&{KLBfFJ@Yb;k*tCrcd>0Hy2^)Q{TOo(_9yuTFunDoIy?0D$*zpfM@V6Qg9& zA(wSIj4EhY(m{JHH?-Peu63SMcx|6V&89~uD`}aZwXQi7tihp}^76ecVFQ%$YFj}} z)d|Xh0JF8)^^V2bt|f9ASFYXPY?TrZ6`IH6q(a;%HIKoFdZHcx)~0I*4jeeRyu7l! z3;>hK*yWdhKA)U0vOI&rT%RuyNyLGC{=U)s>ay zWkB4#Y2#$FaPZ*r1H0}I7`AQOwy>}eiBcES+3b$nZ$EJ0KtSBGW%Ks!JD`JjhjZJj$%A`@ZkOf2fFci^Tv&% z(Rk<1d+xpO9-z2!!^UHeKW<@RVRdzt0wG`^B%l3thLex_aX+x9r)y zyF=W*W5>n~8%E=97N^WIlt$s;!GkL+tLxVBom zZQZ&RI2Hpyvp$YS-FQ6NyMN!#yY5+DSq=#6me%jsv3ZX>()&s3jlEEU3cx-y&C|IJnE>U zjyw{K7{(a)?cbl8nuN*0<>g!NzHK(0PA21{k3M>Fab2QD3eORzo~1VLyYIf)bZyJ# zEyo>y+`{7G>gp=xy=DP`C{il9ckjOYAGmLIWwq)Qa8s zQA`}ET#+5`(gb-vM9`EAUuf{Bu2MOF8_TB4*@(Fc^ULlU&fyVYI}gB)uJA)K@D6$BONukU`X#5g{lLt%A?~vf^9O#5Q6*|Dplq zMUPvt7mVj$?!?o^;4o%}`||%1iPV@7Ii?eytBf`2T#sj3xgQh9wQMUuh?~IxlVmg) z9J~hNoE9~_tfO_qrORTrW!$&ExO(ZB0B?xQuN4L~Gfy5}vrSzLTonq*Z%ca*K# zN;5gxMYxf(7o5%%ELae=N1rK89L6Mj4w2|<(YteOz6hlN;fMevoMlH?yC_;>XjoZj zd%|v&ouKmUA(LLA#j2B3D`oC1pAIS@`wz_HSow^|Wn~D##5wbJf(S&|M;b*s=@A>l zx;s`^rlWJjatgOtopE}-p^e6-* zptTja^|}LLc|t3TqcL<{8uSziLWoE}6ahM%&L~2E@}o9Rmb$(t45^?S&^e)>BG|0x zAL5DKfvgOSX`dajBxiXi9y-f5nEBAFF_%ZP_uL_mPXQwwKP6X&3n3t?lE8g}J@ zMYiMc&Qy@WrP-I4Gl#tT zggkzgCo*t}0vnCS-DuQ>you(BBaV3Bf!+7t_W&TQTeq~dv_AD2Fs!bvefRqBUUccj z|8?FMum8?9f#RkuTh2J+j34{47eD#QPfq9iB4Ug*nN0TX-FN<%zx3J9eD1nyuidwA z&v-IE`J|Jd`OIfN_qosAwryK#lk%3sPPh{QzWAjteeTnryZw&a9)Hf`Uh?BFSy)_H zm@FJTc<_RYF8J){{_9)c{MNpGdpB-c|Clo$^JD+x#e4Ve{?JE0dho!3*SzXgPkPEz zx)3h8%5^ zy7;1dciz1)9-Vm7Nk8y|PkYX@pMA_R$K+VgLb&P18{hSB@4o%^+n@Km=REIu&s$hn zSX)~|#HFRBFMs*V?|JY0mX?-Y{hC)l?(DM(@$>(6?ngiNv7>kFc*#p&ym$ZpcfIR9 z-@59maTks`_UNZR_4{A^;ujx(+zHd!6aZp`(WnD}tFF50!yo;8M!ty?_j ztg~MAn%5A~!oqlUWrZk4qR@5I>GT61_~85A|GvjO>QS$K?Ju74kW<#CQzVk;31@ZX z<&UoGZn*Kr4}at%=Y8Rex8HUfLOkcJvtIZ5H%zC~QOMh`QVeeI-hChZ(1$L$_>!mn z;19g$MK4@hzaA0$zQ5$sOF!|+PhNi6<@@*SU0Ap9@Q0o8+~+<22Y%rDmzLJAuCAuR zjWJ$$;f4SHssFs}vdecpaDNElsH2WL^UOy-?|IKV`>eA@qtR@-HX4n>Xmsth*M90h z|MTKYF5P+e>692`8NJ)Tci6X-|9l>8GCt04cp_Z94tLCqMbcFMQ$b$DZ{=KlDSF zU4Hq$zU$xaxb60?>rOxIp)YyKOP==hXRKSd*!O+cbvNI9%fI~VzwX?5*Z=s@A3g1% zr@!ZY?>qm?Uz)D2Zr-@@oX0)x#Xt6Ar=M{~Et@xA`<-jw|A7yF^IKQlxAQ&#`07```rE(tJ0akMPB`Hwe&XdPJm~n? z#|tjH=tCd=$Q4&yx%>Y678e&EedeQ{|NIv``AJXOvSo|iwuU0zc3#aI&pr}WqSjV@ ziM(np+8YMiZYh+~(bE`&dECBIu+%p-gF7G zDPHAzSnng1An?n?(gmrXmI)xMD#&np)odIS*I;Sxb(Oyz7c*lgFy$BOadO*5v$e5c zJ7p#v=wDgj=Nv-hus{O~6hm(NDNa%F{TjA+9<5t45%Vp?D4VmrHb5<=c51JMe~gFY zn<}vMy%e)2wYsLe0tM5Gdbn-2u~+HNAZQK8Ob2j8ut>)Y)+iPv=lSul|7{bVyF~@+BO;kc6iS%EARfp8^W%QEBhvsrAaz(Hc%jlX-1EL5wPoU?}6EoM=hQB z*!AN@Tw6K_Z!fb(W*`H0UD);LgR$SE*}I<4IWZiek>1zlq-(lWg+<`-^w;zF-6%$!FQgJ@${ zR_y`zE?c&|k7gI*RkDC>AlZ`hE*=s9pv8rSQ8!w*H0}F-@4kKS{HK4s_@aw};IU_& zeab1P^nDB%X0zG*Kk)v4`R9MS@Pdl~U^MFb7%9@Z=bm@&x#zv@um1Y!&wNH7dx|s} zk9Y6h^B4d3|Gn*Pf4yh-E&!M;OlH&B*S`9-4}avtuYUEb-}r_%o_OMkeT=b>0DuA0 zc~V{1efcY2e#7fuf7xZ1Z9C$KXFcTL?81^05|Bko6{cUf1+pV|WD)4{qGoSnV zSHHG#)5cGK`ZGjy&RJ(Y`KeDu#BY83TYvv|f7kc3d+)pN;PT43pZ{Dcf6m!wJ?A;k zSzca#_j}*_XK($p?|%0>02q%))9LIhU;Ww#KKQ{G{_qdK>381r=rhj*oC3fdx8MGO z54`WY-~I05y2&%2^{mlow7R;QA}<$Rc;UPL?Ozuc7M}8??|a z)-By|;|<@q@*B6@c(aVcCqMZgS6_Y2AHL-ex9`|KolZwx*Z1)=pZ(15{qNtq{PN2L zov(iNs~2B->G3BVw=f<9K;O@1v)N=kzVNck|MLI-ziY1f_Lsi!g-x3_{qHyZZlOWs zn41tnHyVBG+u#13H~sFvf8yhb0WfseTz$Cp~p-Z3Y0-+1hN@1Hj!ocfSAq z@B7mE=ihzzolpJ#?_XNKzVG}0_>cd1<8S`P_19jPhIlsX&p-eCkA3{(zxu1c_On0x zbL-bH0nltV`^ZN=@+WWo({FwADhtV{{^Q^O^FKfJ8*luL=Rf}iT^G8p`|?-5@`r!) zN9TV2^S0OrKls5XJ?TkreB&FR`<&;d4__TTxc}cj`HA^IJbp995J$L6l z_l!r~bk<*d@kO8h%xB*6mbbk6Rj*1XHr;gdjqiNtJ9gfE_f=P1xwL-$7r*#LY56}s z^{KCX`OAOxSATWJ!yXoiLKgyti!Z+TcYpu)KmVD}q%vLCU3cAepZ)CT&O7hCKYYtu z&N=6t<>loN!otGBU3cI8|NiQ2|M-q~?0VonY4iN^&wuZG-}AFS`}41V-Rr*n?Qj2| z|MN!x5JCq4*I$3#_19kq01rC;gztOe_nmm+gT8R?d9Q!tuYdE(s{mkWY2EVj%7qtR z@bOQ4{3l-a@?ZUxUwQC}C(im=+7oAt7Y7%TT5Y>Vi!nJ)VcHkqAwjN4Oc~4s??FrS;!V^XnW^Qmws9Uw$??BUnu@__WpfNqB>0Fu?t+c zMzGyCJ3RHgK%TT+ZvZn%0UE@XpgKReZmE1?Os;rX0!)>t;(5>~^JuKZImqk<3JD8O z*ZcCWeQ9F=**T~-Fm<)aKOb;c)R`S>wu~OSRvo0a3xn#s@#4}oZxo&ylis8v8*$c| zUxGs*$$m$TIl5V-%O)}*;(9Dgz>PP`4_d`rF~T}etSE5k%ScLH%W`%p#Qa!JM-vM3 zb5LAlVZ365#(=f1EFAN>S%#2*S7cS%GNFnh5`-ywD;TSe|CXO4D+?SB4{JxR@L_El zDw}~ky;j>X*a$b25FgehxlBG<$R2u!wCT$>u0XBwA9Z zO187SBIJ6|z2X3%nI<**b$+kOMbXQs4|JwMmyY&rnF9bA{kW{LFtV6gnp?Dykz1XmYRz)bMX3& z%^8~K)`(wGmhn25kK@pX%^h&SL`rQO zRVa%@%eth6AuBaS20@iAW_~9%G?M$-vgz6l5didj$j#R;{Q8df9o_e{m9@3y<>gB* zx$Hmw{S$k3?|%5{4}0~iUv>QP#~xf>9*w%b@BibIpS31)??6L=)aKfLx z_0M1OvX`XPiFd#IJ#T*VoA>YEzhT3M$DaAvhd=t^vst|OqD#K~m9LCP-DEtTOcs{b zZ%pKwjJwU7Hyzl2@IOEGslJa-e)5w~JMHx8>e>^Y@Wj~n?|Juo-uT8h?Af#D#1kL< z#P56JNhh6j-~A66z`dg`eF5CW{}PA?%e(R_Py&} z?|#%H9{o$d@>(M5Mx*o3|I)Ah+OK{0`s>%PUw_tFXFu|hj{wB0uDtT=7krhz4rx15 zI-7}zmRFaNk1bj~xH6sfqfr+rvhzYIuZM^x($@B`m>+^^f$lx_X*lK>k|WX0XHlyjmP8lOB>Q@mDgNz%^&}t|8xDd*PVFc zNl$#z_nm&mX*YcLhI2oE?wxnu@s7X$$0vW^_kZ7$A0PYvBOm+N>tFx+ojZ4K*s$SI zk9zbYANjE9w7>MS%dWZl>Tg{BjemUmKRxboPdMqM2VZc(1+RPk>n^|avJD$Io_Xe( zr=NcMcs#o9y6Y~#{2O2U+Sh*hmwsvT_IEt{InRc47!%NVJihy`yFc*0_Z@%y@vnOI ztB*bA=xd%e8>Xc#R3>wNy7lm}%OAf>MDRCE`Yz?k03NvOWJNo#o>p8?u z8f9cxtx7qq7NbR-^@t9sxlDcB&_ycJ>^eeSvHfM~gJbi1f&C+O{Xb%cDPl2Rdp$pe z3#a>9CCK5?cZ##9om4JYcL)u5($*C2aCKk)(0&j4$Lou-u_&T7?Yi=u~thay^Q*ryfpT;$MU}3##{N73!0O`#SaW0iK4=LM;DW?JfoYv$^Acd z`@c}Oqx|Oo$yol@-*xTVs#m_JNocpRvUC1qH!i(96!0ld|L<{^t-Yb_63q`hmE~L! z9gj?^n3zuB6|hg#p>V@y6x)@XIsp@0>oZ5AmpOD;zx+IYf9Jfo=nK-&{z~S~`*$CG z2X%rziGL<|N);^aXEHLIRBqm~dKSfs5A8_B4b{wK6#9?Q~amr2FY8qRzz z=Ue|}{%%DUNB8942{S%$HJ=N+`)oYzz6JYSztp#$(4l!~`Rv#26vMvz3Vrs|pYo_* zxeOmXEe;YqDm(bEVbA2JNti9|K#Mc!bLu>eKZgz@t@+-?H;3O%a+y5cmn^!P=UiVI zW?18*u6Mx1ami&gx`e|;sVK_&_xH%65cPpeCNDqpJUi!asOYpyzqqO(@%7Hqe_yV= z_?!FQ^T7|sG2t?s5EH{Pk;C?g*L5&bpS~qEaYE3D4dq(QH51dlTwG_b#qnI=%fnn@ zVc}8R0sGctn_ESt?uXbt8Ht3dg49TYGbufACFDvFSrJb zqtw<6*LW|f@qMQ-CntaTl4&NQ7dIApmZ|XdIX|z=#|vWmU{U)|Y_D3WwU+IS>B9Ze zA(t{!)6>%9)v1Y#Z_oeKPx<9vpg)z7PziiW%lW>kp3}_``-eN{{c^MX+cM_I7Oc;o z&+qD-D@Y-4^eg)N4wfE{-03y^@Vd8m==HY@fuH-YAU(WG`b9gn^NB&*U}YK8qI?{2 zBNtU_pA|e}94f}@P5Qbsl3E3{fD)|w949|(LJquP)$S9TBz~)jxV{Q}#Y`^RG8eXA ztyA83JV5_M)=H5z7>PR}Zdei>?ffqU^n(WwxarK0?Wmk96y!!X2mm)3RB1cOeWGwy z+pD;#TPR7YIeUER0Ze2G2a7g#tJ&?Hl}lmAw**9~>C*f|Gdhom^@AVidBx z)*LZ4Rrgxucg_tXO|Hkf$XX94tc204k!K-crj2ma8{mh^x@)#g<9>6OA zLDN8TFR50X_vZ#JRSASc5zgy97O${Z0eTGp_HqCuQT)rxo6 zIa4?R2<_b9={d+zT7x2(TCawJtAYe$X$$AYQ52$aJpo9k2UX5%iy~;ji-DfVMc&-Z zENDXza!f;55ngEEyhg%F*p4Eg5rpNl8FR?$6($SsZbN;)wRIySBjTAkF@SJkx!7nm zF=$CRmVvIG(F7cQAl=RhM1)`$h&!T0fZ2k(S>-5?1~d-1`y@d)`rM!y7@mzJE2L+~ z&99RUOn8Le&0#ha=Pa5)6LN%DMm5l=;OUC8P-9Ul`=<~9BoG$ul}`kTdyu7|mG+5T z)TzpOfr^ERi55cxXC2-kmoRAv$Gib~w!ynJIB-#1-L3BL*@Fh@I7;~_FTL&W4>&QU zh(t9ifJ*KaOW(`Nu|JzD_QHoHExsgr6&0ncoqcb*%OZz6k~e)uAU_uhG4`n)e@bId zLYiYoD&)Rb+_yzC+*sDw3-I0fabFl&HX^=n_c)^7;6V|8D`REB|1UV54LUqm7~a$2r+Gn;Oxt34wL|2kN8p1UU=)0o2Vg2z*NB{_UZh;5$p z6lD=~4&V1MU5);7`G-8gJdIo3+sjn`_o0<&gC?8(gAd$Is;O{3-W91zi@$6`#NB6U zkr939oo9c1UFY}I$G;EAYPCJ(G24{TtPj4dgnxyKvF761*UYp0kUU}%bZ(H?yi!1; zTgdkAreFFEZVkF=aiPlibA0 z%jr8LuEn{a z70Upg@ekxT7ClGX$Z0sv`+Aj|-Pa2(F&i@+HR}$sNzozaS445EOs!&uxlf7!Wn7f& zF|T8Z`FtHpIBRS1g+t)MBx0F*fcQvU44Q}s%qwK6qKy!hgD*X0|JjXP8^xWDh7b)x z-t~?zObEc3FP)K)D2+*hfx{GlJ+SkLlLmJ8u1xqbxhSN25DQIAQlqfMVrJZ$SMTx! zplWjQfuL$A)FWnFeBC}G-NE`R16hx6fCmhE&zQ?_#A_CatSYc3@~>a(K5z8oK$C;1 zsh>vfqCE_sjI?c*#vk=6`*i97#!jdeBc*coH5XG1&E=4-Or*#?4c5ot;aIruvU~3> zqoDOTK+q%-wuyd2HX|Q{zn|`y?F!6?Q=BPwh{^w zLWWB$f_EM=Sq7g;VNr;MI;R;FyVgGl5&CycM~y#eBngc{jUSEjd9}nR3xtOr@44zKia?y7m(FwIk*FJ;{Lp+bjXl z;Neo>Ke8<3WCpT6)SL5VM3Ey=vjX2_*@zu!0V^c|KIg^QEYiv8kz>y3`DSc)-pl{u zH5~0Uv2)M>ud>_QEJ&P3lEa8mqL4X#jw+Wyx`le$aY!~g!p#gBAG+E!>c5-`Ar5P~ z!t34OP^1H{)Lt3;H(B4@!b0g_(g4sFqe|%OOqO2S6sy``$^@rDO%pQq;iZug9WoEB zxP|bLi6!4<{CHyU=^q^t@Zznu{a(xIUgPg9B+g6K8%+OB@+8Z?qP+Ap81hG(AGRq~ zL6|p!1uVEUC>^!qfp)`fwvr+|oCtLLvkI|B z1>Uw722=spNuU=8ZrOp${@meFDr;6MXipWe!yji5kqpfp$=kq=p8%ixpeSsgo|E`D zeU#Wy0(zJ7&XW=RVjKDps69^h2s3*YC%Z=IV3o|Xy>#g|Oln2Q;++1XPuDc)sY05i z+2b{}IQ%Eq)-I9UcJF%D%L#E-+2cLa0MuvnPXxBEu5Q7t<*Y0D39ENOF7`_<@Dznu zJGyg!Z22p|$P%}v%sAgMt00^Y}3CitjJyaCG5EUd_!PSE3BR zp0Io}f9B)5|GvP_Ga}i_;aj80SH3oLwcf^GHqSS!K1;vo9zSh>*x}L!s`CUx&16)P zZcp=IJL}rtnsP)Z-Q!9*aT=QUzg9Ciy-Iyy$f(*>p~Km9cv>$cA!{~Y&(QZWy1kZn zo1s)Aq2lWA%I#V_wN3%f=||jpo3j;K7PsTucH-2%1?o}z-?pN z*4%?XqK9tBxs<$S|H~&MIST58Un30yKRe=|HP`vcTZtORQa4{>7%inV5WfFTNH;Yo z9GBSfrl>uI!jVBD5{ zEzjo*-%}Fk2*L`V5-E6*}?hri~W>if@HS!QG= z8Qeak_`I?)7ptcxq3#-wK8D<&ymUEwS=#!6ue#Kv)?54khPv_{x^FVSzCjl4eQo_I zL*jS(b$M2)QoNHA{4s|2-o>-S=5aqN64Ai9f2g;mOm>+tNQaYc41%|zg8;7PKN<)R zJkgWEBRTP_n6o_y{tVoU7nee@hcHBo%%T^Tyw z3ZA8duO$^bfdLpm&7SwFlq0ZSCd0e)|IucLV2Z_ifQuK98{M)xZ!$K1{Poo@v zPefD{E#8xZI#?iP^k!X5A|L}B>6Si#7+MNhV3#w9AtAwJtoc~Ky6_M9osF90^qfQ{ zDxW+Hh65rURIMGadi>7a?2&z@z)oI4r-Z2!1u^-tr^?r z;y)R1{6Pm(YL}LlI?vbcXjzAW9e?%>}zyAX-lb zfJPaaPB^C!W2bvJp#H@FoR?F4&wc_*9?HBg2o#=MRCGH7sJj=o9T#GVzIk^`T4j>zWqM1o}JcYB3OfkX;=RC-^s* zV%tC=_!@)-CWXhjU8vgz4f7aI8ry<+hajDY>|{J&6r|ZW!sZn!M{L)Pkv&Tb)RhlA zM;^K~J!nN49|+xJ>N6drj@Mx%?KZCeoj&judAaylG`_54@im$Lm42>+U&W*s$HjV8 zM-f*d@=Au(szqWBfZa(zi5XMeor-DY=s}ZgJIaGct08?EGnb zm@^5(r4la|Om)bQ%^1ZA6GgLL;kGv)TAMa(O_+tV*|`3O2lx8w3h;B>OBJ>y`}|9s z>sgR-(KbiUd!6tW?Yekb!o6&Nq?m+~`rQH3c7s~&-w#{9{q+w2_}~3PA72U&VQKX) zesTMPgAUc^+23vQkHu+Mj`#$nt}4&%>)L+1EFpQeOyf29=I!sO@*b|627kQLa<8wk za`(aP40WRa%i0WmXYn~YGoJ5`QFnMI`+xII-qJX?UXyO9UXivTvme4Xa`bQcRxA<{zJA90Y%=-m8p8t|47;-jd?lWh6fwYyI6(bXVrHc^Qt~A zYfLHV*4GK|yJgG@s-+&DciyDD-+|7Qm_a8T(tSGTHHE@Z-L^^IdGps|7zFU9y9_l# zeEa4F>)Vth0`BA28e2(gt3%V;ZQHIrEz7!|Cn6n&<2Lsy9{V%se3Vw1Bfh2TuyC5+^JaVjCmaLr_3pAzK=PUFr z;}j&%6|a-@BtMh)up68n&bwa2!fn0Xv5d$1Am+$O6cXm4?syCcy^pTRp#@Isj%Qk~ zH7mje*F29hnhf_$Er5ots3l z(rG3~DKOe$aJp=7x(vK7D+P)mJ=(UZ=k(kce<%0oGS4H%t7_ZwB(huko+0bhFywRt zA=e4&dA6mD{Q|sY5=#!a=%`#|KBxcqV8|LI$7i<~;Qh-m2qzCmn@#VyLcIY-k!8~+ zX8Pqzp7FJ2@90Kf;&R>2?w>yx-o(Y_c+Hv*kgV%+*DwB$ZIs2laqml z?&KFR9f^p1Y<|`*#Y4#*KK3J2Mao@a^0Cj{Xe+hWAckk@JL?75)A z@jZ!=kw3YgnFIieeu@df-${buoPk5)u=DG%cUa*DGxzRWx#wNID#(~bH@N#yaZwiT zZ&46Dk$A43b+IkM+s&7NdM)sJs(6`vgN&@JB~2JRaz|C#$EDlUnPxck1^QKL`VtFr z>A#5^mqWTI(bkx9;l}>svl4~Ez=!o`-5mE6=ybkQR41m~d0a#0Jp0f&T7L{3{Ap(? z^Wm@QTh-iZK96>%C=(jfa_%!TJr-uH;y1I_l`{UwB7_gOj0yc+f}$JqUD(0{DV;VA#H5lmHVix<9((9JOx5+#t5=H4}s(7O1A{8PH!Z-87q6H zkas<=^g8)(tFJQtc4QxwSvJy^f%3}(q^=7~6G>ef`=_}Zw12g=4VQ*sSx|>Megh@B zJ2B}sagD{@CEML$MP-!G7j170G7)8Jmu1kS?Qsfvky}De_nvkcio2=(>e4J0(iYcb z=|6=%eVj7+-sYs z8%!xv0&35$|6rP-GAR5Ho?%*ThkU?Cl=(1h#dRq!K4M&f6FT#2Lhi*+)k+{sLxHlazXyJwt5?js_tue% zp8qX(SNW|C!$P{Grol?VrE2+LqrLk3-8^4cAbN75;eZ}Ez6 zNd|qt%OE{l)Wb7#H_b^zQtOdUY2%NN87A)wU1fed2$udxLSKpwtwJXRS-X`GZI{gA%q zcpcIX(3HCWjeH9fd=NfmBDdG)O3HYfKJi5<1{weZMtlr$;51bfmXrdV$N0*hvd-`o}rYzp( zWsaw#GZ-SF6ZkNRt`T~m1Sx5`&ixv$Px^W?UUQv}#Oref9WUK5CPX^(NBiTqQ9M5i z-jf{LOABbCIO#}RL@3;PWaENqK1qL5!$>n&bH!?;bbO9x<~_UMwfrJ5@q1~#{JMbM z{lCo84|(1{3I}UOqGrJ)0}1Uzs=fz*YvU7N8WjOM1nFz(Y}B=zcVF-$rct_}ou zheioMiH4$=;H|s63WtsO!za|6giiRXdJXxf1uBocVv#K8TCsUr=(mj1|?t_G+D|+` zDurZI0qq>q%U)yS(kx9Wpz%=P zJi|7ehkM2LKJd!LP=614-UMu{)4h*MfUI<~2(Tmg1#;!nFgLT1ZfbZNazw!uD7%<~ z4xB7oR^G=EFcr(FZ7r3zE-uvlnHn0GDD#6SM+z(B40v9KJF$%8+vIHyhK6^DUe z=|=DBUdKJ(^`0JP|HD?nHu#W*5bLGfB%6LW($vAK<&;hxQP1_s{c#tYB!|HcWnIO4 zq8H_BOU`m;`&WH_YVkW&Xy|vwj4Ikl+cZgwybr?-TaF34_x<_e)K=a|?)Kl1?YW{& zMM=$k72X(M_X0J(+t(oGhB3_!aQqwQU-s?eSQVY!WpyszrxXuG9^4t2JIpNHOuRDL z#nC!I=JNAu(ULmWeBqpWiev78xh$Zk9JBp8P+rmNZqgCoXj)QKXZC9=)g&)^&esNU z;uO+zBxZzd8lxYY+;xvqU)X0+HtFu{t#BUCY5TnI4*ySe%3BoP63*gT_YrCcp|+Ar zad$GfLD6mSbPD@&Ux!9{EhEOKY>TvCC~tc*wBPi8)J1xzgd(dcN6hT6g-5g*a9d6Z zQ#lJgV&?9&8g+*4xI)Kcl+xS6$nsFR#>WitCC|OK3MQKKbd{5tNGLQ(W8PE*O=u62 zV@pQhZT_{6R*F}ng;X?BMjf;+?fRdHU9u+WkS&mkCjHr&A{@>f_Ws`E+fj56o40bz z?XznRx?#aXeiAoHuh!n&^D93ZJXhsh4{LH4GmJH4zCZCRGI#T3cQ~~~zp1m`t*dXE z)_G|yP@_TxXPo( z;Q~6-Av}yc6-xs%z+I4j}wG@qmQILN(GnaX6Apf ztQ7b>VTjH&@s)|mrO-?_s3ZDN2?#=T%xo3=AB4G1-dSlDA!%nRQd$!w9=jbbG@Sry zatlZ>j^Ts@cmUwxkl&N_v-FBU>epUc+4bw!(IDkZY9?sQcr;+zL2}o=#+6~Z0{#vS zW=dqdpfxD({kGN3)gGg5XcCe+MWDhRdVZN){e}`Lxl@cs zyF+K>88E@RxEvH_y7J^B{aWjrV@a8M>3NzrrZa)7O{1M5X8T`;XW~C!)`Ko)oA5ri zq)-+W-zvL7x;+_Zl2B9EreGlSH*Ij`=IIh+eJ~RN#HhPY6>TDUo#LgBCiyL>xkDD;jO`ER+pu6}zW{KqC;$;bk}zBEf{%;`R{#$5joO#jk+MC}pVE^7SGcKwXsp?527m9P z$kMI6)y-usKMTZe+R~D}a;#x$E#FuJA9EK2b0DDbU0#_O8~?*(a6mHI09xhT@-nXi zaW0W3nxX6@`)~-L!U2isdjR~jB$b{(rMc`2{~%M!|E>0Xs#(2dpySCS!9J~TWvGdKI1Dr2;5!)zP<(-NE2N$e+>wxtFwK@PHmS4V z7J}Zt^Nr>#AMR(_ajU+_X!eZCTQNMc+tMkK67oCFV>Q!MaB4Pwi~8q~ z0sB%n+Y_`i|HV5+#C+qMIVbV!4L++k29XR`?_Dc5Zn#xbXJS+FglmdvqK_9`AX zVv%(NF*hYI{iaOgaV+tfP9u}#xL(K$e7t)~msaidQ3o?!-m8jx;&l_D>E+*$j!lU# zGMO!}EYqZXuS~WhM=VX11pjj7BeOuh&bB2h+2{_Q>kG|gdBvpvd*JWn3*P%1-GofA zIa51D-B9d<#nCleI0LI#T-x_i=axrw4n1FAo&A*d@#?hB;*0KT{OpozIhXY{?M?Eu z_6XTcwr%qAYULA==Sj6!)unl6@3-5xNv8@?sFTL_Kjf0Izjq3)%R@Dgk8$&RO!Zt6 z(nZM_D$@Kha(=39(hu*hGNKKm6y`^Rg>sGs_dkEWUsgq<_f9c$uDyQh8;Pi{c zt+7*}a#yN4W!Y8oL&(>* zu2cuxXnfZ?hK)}nneCc?c60b|K2eW)!2|Y*%fD_fQ)BvsokS+J6Sq#u8PZ<k)>US}jMuY>9_LA_~UGz3zzT;w3a^6b+}c-}cHXZxqmS5+h}xNMSdBK|1rG{yR(-e*8T1v$OYN zGDd?oC1e(le&7Klp>(pH#H&>vFdDdw6h-jmPf4cQI7K@Y_x?v-=)|S2!Sj6NVKXo{ zd4qoa=D3r^$=3$xSy>=zUk?1AJoPAWEzeQDt?S+uf$Z0Wd#?1WYQ%M06AAc|snhYq zkp1~lNWvGU){K{S1_i%D>mlGkEIsfue z-i9*aF>#&oR-LF7;<{D!>LXi;<5_|)ZZLBhw|Q`oU0|Q(4#!#`)klfGDmW0m+SK$w zmlJbD9=)AM`q@qK+(KBNyS1$ zmW-%s4lS9WiyCXe8XLGjkWzDaXyjYfv8?!z z3q$u}^vTydx`f;^)oMK_uFp7~&E0QK@BP}%cCSc@Hj7Qs4Qh^N((y*mUxuE6<6n5MCvh(Zh-qt}hFz)oS|_~u zoC3Z~bWU$dirblfKCOMEt)E;S(N66+WuvNPrt~cQ%KY|7hN4YY=+y`HfivaRc{r~0 z&{^s)jj!)qyT2pq^_BZQ+xtEdhUxr^yv>DYBiD^KFH$LoFH0FY-MQx*eQ2^bv1hfU z5Z}XGZq0ZFw*17sD3+^EZCZLo?&)n-E6(pXNnOV>6IpzX63Uh5*lYOljX@dro+L|! zG+xSfX7qjV#zwE2(rhDtMB=UIetfld)?JdU_o#1DnX9Vv^K89bg<6;O1S);swuwoV z6uP151u_+*|MNAD zzFXM;-NG{Zldf8_uFiigTG21PcDttkDYiaE0b(uP!sqB_&P!I6m#?#y7;dr-IWI{5 z^jQ{vV;x&ey!ewFT_sU>^R5Z~kr?LPO`=-d@0cMk#*7^b3mOlzZbK2-kt7HqZ@*M`dpuCn!9@!C+^m5S$jI+T=-R&wGE` zB(PdnKz3(tuog$$A0?dX0MSOg%%UIg!r(^`1O#Xc<-o%Vzeoq9WOB5w zK~krcIMsTkNg3e)9Re@l#AjGB6VRN5Wllg$-m=X->Z%BuwBb!bdEf)nIo(Fe!i=uYW?R++Eh-re4+t6X(66; z18YDoe$ih4Hf;TG485EGp6Nf&FXXgatm?g2d+;7J1Vj*yL1Bf|bDZoj3X^(}n4?fzKhGbb09Y|Ii7kNpS6L$=97js^rP zBomz}xp_LpfNpZ>B0mm#ESOtq4_|gFZ`w?^IQ~4-imC&o8&G2`p&~|sufg*4I)qRS zyLVmx?&pmc9;%oywVV03#PJ6u7H7h6j4&uuG`RIW3*SLtiVGUl1HL5u6er;bg+fW{ zVbPp-^P?2^x%GqAjBbttd8s9C|9S`tXkEAe9XTik_z4h$Os|5Z2XomdW(qiz782QE z#9&c~^Txa>Wao~kKXHmv)@L6DqBlZ@0o(AN3E!cD!^7#vJ2{ZiQA#)~4L+gH_t6e! zALF_oX^_!enYX&_&m7}mV?+A8_|qNnh{o^8L2gyPjw$F8S{d9J$RY0!5x5aV5K1EVVg^Vw?SyAXP#$$=;wM|>`28rAbzXhDIgV}=#Np?App3NT3q@K6E ze7bT@8TK+O(jK#YU8TQC4ar#6E(QO%`#{*KlJEBs{frHz`r<31p!v_dlg1>j12Ba>G^f6|-vj1QXvPAL1RKup^JgV;n(`&;YnDQ7>Nud98%WV=9Zwcj2p))gp>6>2Qky80GT*52MmnwZV=_CeMlL_US~ zH96Jwxaju-rs1NDVm$A|G=vqE#uU1)we$7|@I0oxBh4shrzy%oLsx=%;n54+4a5P{3=Up3d1+8gO5JU+0Phmwi$EXo=uu1i(4siv{}sG zjsBYYnA+IJs-q>=Y$n#z?G?vIv-NDu}BnP_1IC5FnBUElN`}(9U zEG%f89;Xv8K-*R5e*5L%Mt?FtP>Iy$W)G63$#YL^0mM^NjySV zT&z*c;3oC)e@&Ne99ZB~NlX!0)Q!b}!Lp5$M|{VB`5^}jITvLx+4Ckb`=vh7(O(9ml7ZtgP#MuQ zdI3zSK}+5<_(jNt=vKDp;Y`Ru@+ing;3TYaUi|GiHirk)6IOav2vtCL&Iu2mYT>|uK1AMcMHtwkfgjcPP;P74{rIof z?xcsk{hf)vPT*S3frsqqsNhywTg4 z3JX*^;yk?p0^%xxh%jUbB-&l1`{Dgz!|MOLr!reO5;`|F3NAHbbb zQUS#PX~z-q90EC+f?#*Ci(AWs*#~GS0&m&88rdyt0gQ1WD9_wB+%Yx?jSd;bs+PstlpSQy%tF(P<^A+iH4WH8gzE6nJXqjYS%K?|ugJP!Jp)1fN2*V5BPihclPY z3*88tMJk7a9KNTe63QT<1G+Wf>z1qNe*mh=sZ?&j$_EZU9{s@i85s7LAaMrxGXL@M zzyy5(!?wVDXLJw&;GP5>m|<@M`vM@0>{ZTo)OI_gXmZ_8e@3_N087NOXRe^eR*v_k zDB+JNuv_Q)Ejba9J#230O-zLSDNaB51}K(OoQ)Vx3p^UYXoKCp(7|<1beENQ-{T(l zag%Y3R&`hfV`ALNZ@>1(5#c0}Z+ij3XOB<6B( z*riLMYrC{c)JC!QFVTuc!OC64{|FT82mjQp9(LQIj~1<#AWeHS5@5U`$n>7X^PAlb zvDc?$)Kfj}ADR81ZVq&6I(7MUG;H$wY(#XqK)yA77x4@Z$p$a~ppF8T1K^i}%K}B1uw^sy?p=$IHW!6c{vb!<{3x#i z|FsC$n~O^X=F6fD54nVOVAY%h+TmWp5zvQY_MqorEJ*ybVgNmK;y4N=2ZRC?Gh5bc z8_1KdUD$2C&JV%y`_%h_ew?sBZR#4humHE91i%n@KZRhdZ^+C^4tkM{9OdTbCaQlx zo1|N7HXxQzR9_EUWrMB;BOq|_*b@QPVR??^B*xT?$YcMh&1^XEKP?FxmS9G}b&#u^ z<{cQA&H$^?eZ6#x0DygQ67az1vSUefgNaC{(l5nqgDFIDyzy0OeY!^1`cofo@e>dG zEiCcClX3k#Qxsn|8nQW$w#0{_ErX@#Tn^_vd&VVl&bulC#zE6xp1y2XXL7585Ddo# zb7Hr9El<9}a>L6*h}iNF6`)RBVT0{Bnif3)s7?j^m2*msTbjaP89jfml^Ips)E}X1 z;`I6xk0g&Oo2q>OCPhEVk-wNvh5bbQ=yhcDquIEY6^4s2gGvS`B||QdaomN7_A1ZU!X0;gzYcKKM`1@` zB8mayO!FS}Xh$Cs8$HmkXGyHBASQwt1_WINKXGwUI_0Lp$6H?1fk+`0e0@XPx+kdH zocM96EBj!PUpxV%%9mWi4hWxD3J3slH9 z+=eKBzPs6NS-)*7kg-Su;+}$+402ySatE$!;^HW7@%M))FtVgCc?=380%jE8C{)AE zljUKhe~BW%cfk(Fw!s@f7c#i9B-(-nx4HmuksCH}s1Sgy_zh1p@HZ%5AoUedf*dCw zR8F&q7YeYzlXw~YDktID10Nfkb=-;Qc^;@ld*E+NrX&+WCL8xEvJqF2G+(KbN^xg* zXLY@MUVFc%Nmw?|_Z(o77myOm&=%KU7@6q(M1rNN(stBpd+vn)L9fw$(`Afh(68ds zZ~pl=`>AAI;`e*Sia)>4t22R}d&cWsA;{27_~{pjtL;y)=EJHUwYNy5b8^1J*@#=m zs*)wx_5G3l$7JG(%;AvpUElfWUHBFCH~oM*SO~=)mW}{jc!j| zP4>;l`xXX1RFN40%S#LKirXs=#c@wwAuzYmm0oK|v4HVt#zT!1@n0`je^weIpEtEc10zU6(b+ANP zigm=+tlqQAA4eT}hnw_t87bJ!6>iv$W3}#dQ z@3rKx{7pS&O)ur9m12QhjAL;Pb7K@P3RWjCmV>o5CQJk4my!rdaL#naAY`nv&;bPyr zg&eMCT)zR6|1T*_hh1#?ka2`5)=E>E*Wyt&kL7fAH_7jOsz=Vh^SO1X^$fhvO0suI z_2vBLJ{(?b!WuV-t;dn05ZokJXAf8_#6PkrCg_y)Js+0V6Er!b8a%LSYP-$kad-!t zbAB3^bJRvIlQ=Mfk`xnV)cCRRb6eMy z(C9}RS2-NMhZ!^KYM@)z2&05@#O(6?91MaGyRlvgpkn|YzY<~u>lhvYIhf-?1TW9v z#^*2TgBvXN9Kow#$t9)nYX^w_ylLW6y}ivS{t(iQ4ng~J$_4nr31l-tYsoahU7gVm zH;oVXv)oU&fn_Pq^32fktf3-cLn^m%6SdypCM10bA(AWZ8N&&cQ7X1gW}si@RQ5O* zgkiMFQS5&EHXOjcwE0%5tZ)jd1h&l53OOHJRYzdb1=CBi0fLwFrN}V=N1t;J;w&}~ zh{vdZxpFdakHPjh5Pt>iWW$I60#2pr7xLT;}CC z%RpR|XO05-QXIlVJQXpJ$V->)eJE#1jEBENcBd;IBJ~?5vPHS_hvSET^ff4*7^9mI z8^F1c3yfP>{VdrkkRTeRd~OBio0FHVo$K%R(`@u{zbaTG!!$TfSE3Y&;2Is}@q(pP z9fnuPQTo_9GBP4YwV%XHbEjX4ieEpc6|ICg`2)w58MPi){0o*cgW%KC6cl`6`#D4u z7Dnt)usrI89QPV6B1kRKXZlJ(f5#;Cz`eFSk&FB|c<3|QtFnp&JV{FQVMroa(Yr!Q4~6e2>|YTC;K}GBYOxj$K0N6Zh?mEH26y zT-#DJMpd?2i-ML6U^|(xoufQ;aEP}fSG)S@X@ZW_i7Pb3b3+JvKnAv>mrGn^s`se} zB-eUXA|cidKke4E_rrH?&RC=;DLNs?|Fjr>M6H#Ob#bJnyq4uTdN<$d_T$3iea}(s zKOzl`P0)XlBjE7VHGtlX2*I{;HX<>i$`^Y%id3EXeCJmI3J4xA&_}?1x7&YXmxv+B zy6e_}AX7LD?8XCiC*ib?cs>sfC;lIRh#Emb+pwIVw^CYXfd@6Tn9G;Fyu133l!>!A z%?BoC{x-~11!1U2C3vkX#_G06%RYxbF|tSnQ?6ng&VYOhH3cF1{)4O!3>L6x0DydMQ9Ki{B!=@Vy59CT zOoWPz44+oxxGOW1Cu`d8KV)_f&DXS#UvRMWKF}OdifXUdT%}F8tiMrZ=T!CPqsptF z7q6ecx65u4y7AVC#;gCiI>tfEO0iNWYR&TUjk7k3F6PSp+aT28tM(>@#mHK7!d%oW z&Emo1D|Z-}6t`Pa!yjj~ZvK>b7uH6T(bY9~#xOUOFw>N%5{}2Ok&7$2d?Cm{-FlN0?M9F1mt{tdn-|HYq2bGO$Xo zT-4cw0VyM7-6jPDx3PaA;&ZwvGQo9tlr-W+%58wV&|8!inPzPTB7zkYj-EEb z=3(EPj7$atyjL`#xHzi>*Vc1BI8KINF$Gv3?ed3GSO<9rp2(afB&94-}}HLpLE89 zpSE}3`P0^xu<$U_FxI`obtO;BJlL*nYqCNYD zkGx~&x)1O}o;n^npQdG$AZDsZZLG~gB^CGDY(Onn%hhTnG_97agNF|O?(hEYfB3N< zJNMl4{^BqH;)*M-IC=7YX7SS-p=8FS*WdZr_X>D3&avv&$}@6M-b5}C-pDCI_x zh|3I`PJveg)KO})Owa@&iE7u=#ET8O{!`P({$=Z458gQU?u*rqR3DYkZw=N@z<@>| zbZ*4H)nOpm(4jDEP>ZkR8uSz6Yp&&L=@cX?`>pnD?z!WR6Sv%Q^WJ^?&OGzXvOm&} zydk31YPDLeN=jm5V?&5;_|gq!H_dtHo;!}i*49=z?xiqgAfAXP@lRchH#}`;xg3UJ7zUYSn#w8ep`~e} zNv6d(E;csqxZ~taH{7tXvAENPcOu=6R7g}XvaRg5AHV(9TaO(%a`^C}L(^&^t_4=p zYFbT1v=|p94Y6FV2#(?C61i;M8peT%j~~C|_#MX&A3C^y{{fM-ptu%h=2P1{i^aGY z1`(NLB5@AtIXIf35fPC+d-iN?Z{K?Bt>ZA9amE?Mv|KLt?AcS!BwntTB`3Jp*kEO@ z1xN+8FMa98lP6CcIC${vv(7AREh}!8%cY1g4;vc`B06#6{v92ex;% z?R2ElEHiIz?%CSjy7{J?Pi>t#^XQpJ&p5KPT#`^(bk?O0Me{dz6(*Mt(vi@(7zZAR zXt`QWt4YWCqMy2>W4GVFv%S6lz`=cc_lVGHwJaG%Qe9@Eaj_txJMK7f`|;y@HaE^V zdIl43ZEX=@ww=duJa+u}9mkLF-LvPwfdd_nE^aM=enpG;jRFpKp@F=^Xip8_$ci~linO4W>2muj3B`h)5Yw9o;Tc{Eac4@&fe(>y5QBJEnA;|>q+wGt00|*tfWKufj=IRi z802?mWy$Wgr3XXX4!({xdwpU4)R`7W5O&vvFk)2n2Y~6-YQicM4r|7Yh<2h;b(U33 z`eZufL1K4JUUdym80TB{cbCWho^@r8!I9nEJQHj81xfT)^i>;bKs z-&-(gWjhfrYs6;=vb%{UHU`C%3X#zZS?=tDhcFsW7Q+_xBPXP@*zUh}( zfCwboLrgV@^bE?HUSfgHDhqd=gHUp;E^YN z_mS&AGJWFRCqMn+C z0*I>Oy0ZaV)zv}VN&A{t1LW#~nRYI8&tlE542|X%jRr;y$y30>EFYXtlZdQVEBH#0 zZWLoCSjl2x9n(0`bZYC=xERhk=Nu8)I(4e-ZCNgtA~cReNlcU-QqweTZEcCjk;8|M z963xxWjD_-j;zO`PpfHJT=F<71hiVMws&@>)v74K{{8!}eD{?ve8CGJ{m4iE$dCTW zzJ2=?e)`*2zxtp5`7K0r_BrQXaHl&F(Q>DL^s0QMoONic)Z$^Ffq0sxt*w)b1)YDv zc|x+awN(%w4Gd*O$tLXC+;jeU=ZMJ3Q>O+PXN==mif(Ohl_!hEV&I|L?@5-^vSh@@ zaZ%`FtlQaMPSbL+7>}ND1`(aw+A2$5ifD}ESU!_p*fcW9YPp<-VHlXWqFlT38h(WAsnLZ?n{4U1tM z7dVl&TrO9uRS}-DvdILsnByW^o`;%*2QClGVdNsRy|q2b#Ka{J$W{4;NQx-#+1%W- zxhW!BTie4hj3W~{HEJW<+aj`9j7N{2K_c6yws`QQq-ZX6x!l>mchCO4n+1buwHk(j zs8-+HSZr<_BBGt0?bS3a>RiF#s%aszd1<#2ZIHmdiK`eh`-5HFA!#6!GU~mtcYD>j z$#qfK?ibir2nd2p?=8&l%!|eX-HP^pEl9hDj`^CUvK}YbZ*DFa!(nCvmp~*mcg-VU zk6FS25w_9<+U-Up#ibYVu$T_)B=%v065URQKo#8~=3WRFI(CV~TOSERA2XNQa1hkB z<Ll*X~Jc z86g0lbcC9Li zK{RlocQB^lZ|q@@NU?UX9!Y*?uOyVo9X8!iQ!qbW)0q>8L4r(|iZz&qco?7T~ z9J%c+YGjcDJL^BpGf^8-dd9vJN@j}t?%WDYnqi4H2yje?7^_QfVGf2tZIY0$0qYrm z4mh*FSLLsj2O0;GiFUSyH|T;Z_`(MrcpM!hq3t_p`xLFVWLWU#e%f=WwzgW)YDef4 z4I}ZWZ8F8ZiXsU=)gzl=aW^P{tv+uw1Ve4fD7IH~ppy{>myy)}QVE<`5LQ3X3Lxld zL?Qr^jQyb4b%S3GX1INtIz}Nj0}GRDQvX&M1|lZfI!-5USsgyaZ}`(=ul?iIlfLiJ z6Q0EX^FQ8p-Lb77`L(mpK6mreAKW?r?u*0cvbsz5Pu(RPjg5&Odan*YxYkM#G}DSQ zLA+5k!OUeZ;t741QyYrY_MM?rbu>Q%3L+uPo@z1sTz z?|<&0g9lfu)oWh+`k#B@&wlFDpCY1%KkQ-my2m{bg>f9qekpeZHqFgEj)Q?lL?xS0 zs@qLjl{4@_15MMknq**Jj03XVs0Ya-*pSL(>5M@?cCHnRFSyGmxqE|CJ*FzGdDmPM4grS}4JSwu6*R4AZ{5*`LUQjKUB zXrP*-F_A0HLcd~!D!u6QNo2z1R0YRz7$?Th>k%`{AR~<ZUlbQUWy6LV1nVy0mj%K9TwNwC!FST?FLldcXUBC1Ig;<8l2R=u(K-VbJbedl%Zv{D5SlPNZ!ZYgaXq_xfv!lD7na`bB8a%T z%br=7#t`Wy(dv)Z12i4l#+b4hxr*r|w4QW|>R`d(1Ki1Cc+Dw=z!@8YwLy*%yU7Tz z;=rM^wS({onIg_r&=TO#yH^+BWXeYtix4;PD_{zZg-l`<$S3n$j+48QC4uXp$Em)y6yD_mdIR?HyylF!N$pZiuEqNjKoYVzu4WD3g8ciz-t(0UO}dII3AOZ@^(D@mvQ` zyhyZ2iu6Q8rV`>wlG6@?Sx*b2#b1|UW|?LjiG+7nG;YZs{D;*IH`6zN2VeV+LpwLo zCJnWr&-nTu+AAW%f<@a&ee)X4go5^u_(Q2*2ln6P z2GjvfZ%p*yOKqqdZ2)n=duT0qsbkS{;!M=fkYe}9nB18`>W6%3Lu}bb^_XL-ISmZJ z&3JJQwKO+0l)08N0zYu*z~+o!fk_bNmFL!npi$yc!3uAmpH;uy(cARzN{*nbqd(&!L;N}$Rij;X?OPW9yrq*R1 z77N&g7(2H{6>M)L4sS|rT*)lsXql-&wa^SMQa)o^ZYs((WZV;xsl z$Ws?(iIWARO{&HAyYn!ND5T7`nsiLIlF2^qS0u`0o|#9|+!>DX5*aOfIKomj6X}p* zBVwkJM>BE*4er#K2BmzrqwWqU&}t+0y08YEXnNj>Pn9uZD&DKrJT!jh_PQbPE?2g5 zbH#GxrG0grWA%&B86=92FlClvdLZA7N5joM~w zdHZeHR(38oU*wo;*XFvznG?GL6JcQzDFz21S$FaJs8V{XkWxOfpa1zXujq>@iG~q4 zO2TQzS-@Nrs($>~YoskyVkwz9jj}U^si=wuJS8cz-atH+!{+D$BvvG< zX)#191R`AQ7`N{5)SW6e#OfLCZUwv)qUjI7;Y26sSJ z9^p-vS8JbRMa!W}QdPr#MkBR;6M-@1Viv2-W!}AxQqWV>vB_%Nszh3ZVMUr2*Xwtl zt!j|*hfPGe%ffOtbwIkeA3Vq-{jsw)00961NklMKqT=o-LMk|Li0{_6?t!nGoYSc9%v*DXg?lMu>kna>TEaNm9$ViIZ>lOqZpBO4VR_(q=HyO?i)Y8dgtrtf-`8b z&4>4FA2@&Gdw+W|Erm9TH~3k_|liIzwUZw9v9=8XP@=p2R-CJ{nSrA>QRrJWTJM#MssQVCdds1;*{|k66rD> ztryumSKZmYJMn#J>PJ=;oYLSWj&N8olpwqp8#Btp3qmd94Iu| z+EXfSY44fOP{c(vOeBGWab;=~C#RT)G~FEJE_Lf_1TCj_M0iaL6ve_Cv}Ap+BB#Po zCiB}&9K>`Co1_9Z^AUginKjE-OeLvl!bEtM+(EB~XyZ+LH0v9SWv{n+8aRhJZtAUY z>LpyEOY?pDE}#Z26M&JAu7Q=;zF$6!WkeO7A)ews`E10>i382VYRo~lc)|}!4_Q5H zOD5-kos309f-x^QqJF&TFWbB5xYGz3sEKc-fpqr?BR1ak+N(&4Hc)Bjt zAF-M6?j9xo7FhFkF=Mqm2DDBdi!KzHy}G+3s1gG{)D|YpYfig$Frof#1MDvRt(lwy zzo})@D_>53owqX-%jo9zTB{tLg6jxsaIZ;?G_Y&EV|C-52<5b>ow?FR3iCT{0Ju3? zRMA}vLh3$x;C9W)ZdfHyGf}(B$QGJNZ?Y&Yn#)iKx0|j*pg1Qh<&~|BQER0pWiIOC zxaJ#rYkTPhNX-)r)J&<1PIE;#Ps_Biml%K&lEtx@Afeh=hNQ$~Ex4`t+whV!m}Tdb z+M}QsT=wPcb`=_%huD3TTrtyL%6<&u~0(5VOnm}5Z9f-FstqZh-dc& z@|34(B0|Zdt1~nviI#ws_g1mzpE?#JmYvsr6$PQwrLlo#BV=QLtJb|uzbZ`3%~)MM zhNTrsb=@$;BDDX|!T<6zKlAKof5)5O^2SeHbIt9?PsqgQoO8~@9{SKnJ?c>h4(u;y z0P&ZJ4uN;~2@(UV#sTmfD=MQ5!Nz=*C?c=fVNEz^P-92p&{b0o8ItqIWZbz)*S_39;ZH2r30#7! zj|-C8fNkb#Lb;j-ZUs0$bdmwo;)U%MBwP?W!Ir3DQ|uZu&R+P-bO@c+{#94&8ug@4 znk97?)EO{9LM{GT15V!n`l=tdTAezlM~K#-05?0Z|9^W$Oig4GELV6K*v~?u zGj-U*yppGIu;yZ-do~$WK|ejJ%#a#d?FetBARPE%_d=;WSM1GKO4?f;pm}C zi|FRXhfLL27jVeWP_PFIY)Dwn+rEUZs=7oWY7n21F&L|$D_30dRP(OT#o)=82FzV- z^6YqZ{o$I<5`;xsoaFHu2Bx)1AdN)&E_%+RFzF;gA>Bwa5fAkFj~>7M3v$L~`@iKs zZ(aVBm27UTPEF(oxv(wZ5E0s@)g+4rfBlc_yW73@jKlDm_pQ#pl=q!E$WRYvAx%>_ zSZR2QAxN-=86noTvcI;L$~~hI!`fJ9#$?uqUZE^*i{s?VvM}J6`qf>>5|uAdy&tdE zg5qlvN?n+WNcDiOFwJF9`LxzF-SyJDUV67nbC)6_QclC!b>&W5*UGCY3Dpf=XSOSD zG_iBJqMip&`6hiB`mwplwiyE(>|JQB$u7wpRRAxspC)tUQ-G z`yh>EB&3`jzGkXPVy;CkRO;IN;IoP`@}ZqlFm=@|PtH%tmelDMWYN2!||XSF>6D2XD`tMsjOu$^65D z($&T;^yu7Oe_*|mgx6jKK@xZd9#9oh42)W;P*coa!MR+?fkKSYzv{UHt#y$kUtytX zJVTD_!Y)&H2`U$5&X*kRZuHHjM$1GAR&}ghb&V@YovnF<(pyN~Q9*`1;SH>9!aX9= zejBnrX3qh2XUqanC3Y}R+BN^ZUWoE&&<_G~P>e*oxGZWu_#LcXZQp^?pq#R0R@(rU zPkZl?f7gf5WgqMSVw}#(IMsNnX<_&5de3&kI}XJdfXaKp=Fs@pUnxWpPKwcb6q@yd zobCkV?EenPs?*SXuR&J5>+0@W%E5sbv!F`)p;mJJ?rpN~lY}F;gKxHP+(Vawaj1FfNU%atq!e#@U7`^Vou@ngS#(YJl~f#tR=w}r=Q&&BA##55HF7zSP~Y44Hoq0bs` z{RltrSvQ?~Hof3?&N%0;V0_Ff_9k75~{OEjfnqhY;SyNGV5L9{qt)GuO*5<$>;v!@SSPvZbz;}HRO}2jonrLVEIDY}Y0f$FF$FN}^ejc( z$JJ9>X4{?7#mB-wq9DhL&FK+nCIK`H^_o|${aIh0B0Fq;VmD=MED$>1-gzsQKLrkE zurWLcB;18Evw)__k6oD2B!FUUIu{hpfmr>jMz)MoI$yv>}_^-XHKm$p!Z{{EaK|;0syx4O6FRa-1 z=5uulue5p>$winyjNfFTKA&fQw72H|EqhF_@~_@qtV2RcBmtz$;*5$wy*Lk>D@O{|U$kB>dTRt4w@{Ts%n^smNi1E| z-3zGBwBU9YJ9DI1Qw=j|HM(eCGw#Nze_Ivofej6X@4tkk*8Fg$N0Qj8mM|w4T#h$X zY)thLGh3p9qe@Gkw3*UrnHEhRC3I{@M#jf@&mBu+c$Wg;Fnq~vm7mmXo%N&2FExmj z+U^`x8!J`38CcV64=Zg0H$8#rv~Q_yR=b(*F&2SBV>ICYv4YEiG(f9X*rBzYaqfF) zY`T>kj4;@(ph^MfvZ>2BW!PDqM`pW)EJQ&s-dyI(OV%*<=T!4q3awT_gHu+L_h3rt z6y$;48)esq12Z8-_{en%Svq85C{gp~`XWt)nkW=+p(F)IFmtWR2;(cRGLtlyS`PJw zIYIA|`#`jaa`L6MQvHNi>?oE{uuHNmgu&R8TF(F~$G$k&b$72x9FnzUN;zK}#76wW zB8x7b5b&D^^vrQEovjL?HOANk>^R3NnuU$Pdy$~$`oOaXYBD;pqdRaDBanG!Z#xPD zge?Fj6oJN@MZJiENTdfeo9v^sL+$Jh^O(f9IA?~YDp7|}-;9O?yh$xK@}XyantK8Z zXNI`?elg+)a|ae92@c+wNACs^h-P}B!>%>PQ?i=*wP+u$yOe@%^TRTK-JD&NT-W0F zlA9*-I$952F!aZ*Po4p7z5i&tZ=cR>{rT5liFYBIY4Qhzg#~^}ewo`+%aT?;#-N2e z)u4xL-vp$xxvEmO@8+0L&0{i)WN!Gx z;5eYsDaM_^0W@liXR^)UeGr<%it zoB5jYA_*fl3*r7FONoGBNPJZ=oiQJsA#?_$GC67@BnU#?YWQ|TJknD0P*!Icm`GMT zL_}}-+pU+q6J{QVG41whRub9$m*N{P(!qe#*8cj(cVWySa9A zg)&jO^Dn$S7Tx zEYnN~yQ>GOt+WH5MO-!4IcO@&zK&avaxxo{hNa^Za*Kh%sU8Wj%xg zxN(P<8=A@}hhcABFu=uH9x^ws! zdv({(sxlSaHnG_{!GLx>T}9gy4S}+-Fl?E&4fAEKOl_ZO;P9RG3uz|!uJ&S0h?3v4PYs!!S4Ne(H#Nl~BtBy&UjxsDow$Dcs{LU}g}DBsSk)3~s(8Rk5qs|I2kQvHaD3hn zp>}y1R*7_amJ>2yT7&3Fhm)z@W~yzLk~3{!iMfbKay*!e0l@tptp{}=V%5Z-NWJkb zBz3nH_q|lpL4VxZBNHkihMqzq0^@D_uL5ghbXNV%p559q#2WB+m39Gm%qQq>i`uAW zJUUcPs@g;zNR2o|a~_cCDU%<+?E4#PE4yob#Uer&Cg!VICX$I{s!n#XL3Et%ap~g! z_uFURWc3NcNlR^iJ54KpyRg1Fz`~K&t^ppG!y2L}!vvryFCe743P) zdHN6B4wl~+z?F5;_ebjikw867bat+biiPZjZ_Wd?K6RuS<}QpaMij6!u$?l7i=<%= zGcsJrrN!)dHS?w>>kNfB?i~N?U>+oG{l#vhBT-Tl;D3xB6>detkeu&%7gLy7@3k6u z2#R)^bi)Y>C9&GB?xl&?qDq|}jg#6m>U5fHnq8&ZKz5ofcCQZ4gBi+^!#q6M$n@HW zUceUiLYVeJmvze6({s0{e|fJ6Y>IxlS7nw+i9S46EJT9gvRvM-v-;T7=@k4|)oSYt z&9SK`14`7>;GT5FbiU1=)W8O33f`EA>KK@#P|kqWeaySnntK5`^5`sP$zs!#rmcCH zxC=c!c+#JlpiGg3>jEljh`GM^SSQ`QwGlCdIAr!_NPTCdlw zt|x@g-m45GNVS-ej=dkMU#hpzuK|#}EP=y@CzPBBar)~m`?m_TAD>t4EJDebP*MX+ zZQBM7F)EtbDX_t`duRaQbR>d#NNB+de_K)>zCWga0;=8+oMy`)&)7f*hSa{5@XCw? z%IEoYW=kZdMmoaZQ+$a9B660eFd0Ob)Z71ylISflDnRK*AK4ZJHfCyn!Q>0la8|J) z3RD|bB?8vF!dG&P>-anq$8ra(8T*}24)P7+2mKZXC$K3dO#&+b;hwm zBf46diAbhmOB(dl`q-4B6RkHXfqR-sLm=h?{pvS=_SAuC_?G{)Z?T7`71d842=Tza zxr7)d5NdcpW}zjo;O7wEvgfk^7iz(hjCqpygt$ktdZ-a#*bAfkcv9GH%X z>2X2cC@b1x%R@}fn@MKdEhA=5nh#cisaN}Ni!fXqqAj7g>r28vb*g0Ydl1i#0j__1 zZBEp4MqgjG7#B6>%6?L zHiJgsau1{m1URMUG$}P_$+21Q-w<#AH^6*VBnOf@-C1m1Q8jUZP?mVRpc4s{3@09D zMao%H3{Zk?3IU~<)Nm$I-+YBm@ujwtL^C+LaHB==XgjEzzdayELlEHz5%5_TX|d8o zMTVPNAi5HCn9U_&u!-=1flD;u`mdWmeVlUe%r2k4|5y89sD|CRd8IiPQEm+AGc$u+ zk?b6jR9WOD6^i!6&0hZu{}yc4Yh-{ZF4u-iG4e9HI!|XuB~53}#MUIkb12qD8?5AP z$rhLt&iGvrrF?9_Zb*rs&w%|P6f9;pVO;IC8?v%y)$Cq_iDel=?8=NTCOcvLA;HI_ zQf@(ESADh^08-;SNF$lKLKp!^ZZqgw3t!`9y%U+?lC9|`$%?BL?!JhN=j`N%vIMB#>fm*LI`Mc*)%sWo3%}xRx{Jsnr=R* zJj~x8i5FM7i{{U;L#fjWlHYp;P+{Bfjbx!ARrL^P zv{&{wX!xx}#w3r)cax%|ICfJq4Iw$EZ+3C`-4+eHUEpC3e6;E2hE(<-36L;O9wixw zW2iV=Qr_1!M*&H$DG37D7&soWjs9N9(gThWcGt7nB0&o>i&X(FZCl*DSrC9-4AkCf zwYkY2ciSrU^)yW)Q<}A+x=h4H)n%$tI!+e3U?DXjy;>5{xPP(#=Fcv-PA-XbMT3sS zbn#FJ8D?f4nMbCP$`R1Q%yh?={K;?Zyzci;F1Lh;rWH*SO%urqR1}Fg7cCH>p(>(; z+i!Hcla?fqn_f#qYW+-X@G`5zAX-4S+*oV(N-M5bD3xM%i%GwFRFp0q{TCsMT}L5L zRmA`AZwoSL#AJYc9Sl0$C~pF>6DTG7e>fHtRq&RGIjZbhRu30)ozznOFESKG%O%CT z6ssPeR|-K|8g?on-WB(-5h<&)=wcRKaK*8hGoj&9jA>G%VTjnLOJ9$aH3E_m4GNaR z$3c2KJqIH!L`?+Sm~H??1(nTdI0#)t*7~&?j_w)^8N|80*?BQsHwPoj8EBHR73%7? z70n!9YkSzT=dF6jsKnq6c;wKur4~DfmRY;^hQY5%eVgQ2X_r!|$Wq6K%@fW1z} zD$X;q)qk3e@7$G*rp$&9Ub>DDW=qyMNuwkwf!|z)ptx1n&x`}}QdHPq$M$4eab+7&E5LFn}@acW0d>Q0k$ z8eNNkWO{}McRD7lTF32PqLoc2474LaQzo0C1jA-3AV7*BO{N_4hL-KrW_u8%2)FoE zOB=1I03ku9DVRN*axNYa7y@E~HTBR9nB3z!uB`2)D^AhWo195&4kdC}a!XZ;BDy_$ z#*r3)d^}pKP$SHaGpJ|=zP#~0Aw>ixBAP^|sbp*dHbHUPaS!C(Y-@#LT6rTR(~1`x z^tq2OuluY#=4ugrr>Er=AcVH=oDQR+lX#dcriP=PFpLzvQu6xY2u20PbW zngQXwpRsyT(A^1n&19!RU{!>b!7P!^)PNn$xl0}q!%9ygm>MNx9<**OKqp4XXpoy6 zNhSXq+*7TE&_U(cPSEM_7^>sijcQHZU0DQo0f* z*XDeHox6c(Mc=A5Z6pLf%Bg|B<0_R}xwe&vo^-Vu!^U^iQxq8oj%b{NUF;_C^Tld5 zgaS}*)=L@O&7l8B7l{rFG4t24g@7)QKSt4tjrA3yh=5T#4>JOV-FrhVFiMrUy_Ms^ z&rW5FIwthr^FS+1ZHoNl+Oi|Jp=r${p<9EAXVBeppE zWHjp!JAGcfwi$@R+)6Wn@MnEPtCG0YN7Tjv?pmPt5H>+b5Yy`%^ZJDB%CMTu@fKxKo4_9{ZzK59FudwkNd7L6@WA|mmIq2QN6g!WP( zz$`&OfO%U5uRiM%=-^U_sE$&kF$T%m!($j>H;ro)+L#taU`};A{gl4uU+1QhOO(1y zrkUpt7aW9a2CjtDbIDlh&;^;1mo`aD+cX;NG(zn=Y`m3B^*SLOiJoW;#g&@H))aZJ zt}WX&5p{N{_ktG#Yu1P(x(9od&mpZmOq)y^G+Y{Ow1+7P5h9wVi9}=)S&`xBuuu=4xMrBJPyq^ta=Zy3Qss z1H-#DwFpH5f5)h3*4(er?!Nzsp;H2)37K~F5nkOz(^4P1tYiTbhKk;4vY}D)yf0Wp zZTmDs=wi*K12%H9*KRus?M5l75H^6ynm8tn8%%)s(-`qv%3GR7S+VV2TsuYbs0x`C zZA=ei8id!o(iWC%j2y^5w)yG~rtlRupWeoVW?(U@Y!HAJJ0*yKrRIoA=ohd`z;}#9 zvfI>y8Cq-T0GwF|w%b6=`1foELmljZb$#rt;_n`-cf8O{!4AdNb-ngV6+H}dn0j4z z;hGoNK6q*{tBX$cm0Vq!qurEa2GI4EIl^0wxvUW*2Rm9?iUK3>GYv5jUfk5wm=b9d z%<}}7c_L*hH&6ul+fJ(CVr~XEK5<+;oFl5pTL1zb;SmL)&smp;V02}pZ>fnwKw*(y z$MSULErK{BV1{!qRMVPaK7rI`?kbrD*~T38OOojHWuHt+@emWV4aM(*mHW=O;dNaF zn&f#YllVH1hxE>k(C+agCjMEYlZw9|m<#ZB8Z;&(V%P+Ui7~iK1XN4KK?=$?U|wd+ z)=)_shkl_GseT%f5t!kQ=dkLXkPj#=mnNnc5FzF>QbTx9{wjM^y246pb@gmdR5B_c z6?V2HiILg%h;+h-y8caLyu`^6jM*%#!A;FtQCEJpq#F`~I!tlgbLgfts2R(wqhi<_ z7|MYVaB1NfkM9y(#0CL+J!Z$1DI#UZc65%=2dOBc1u=?s>(zS<>&H=TH=+TaE*%mQ zmW~W?#fp1On)Mls$Z&_0-31qMToh!-kcFuZ(}{9&gNQzmiHK?zh?L}ULT~rDMEBQ} z9FxwLRLS9c-Fx#-Ua>s?{?p~>55l#_QjG>yJl1%xKulp82D;|+%aKl<`=I@s8>37X zu65>paueN10+GdfT+Np6;zrub*;FRikTexrmJ6<;E6OD0%}Ri=SSWsgVXHc&JGj zQzZhaYH{^Vks<~LN|ON-N+?{K1QRIV_-HFk9mNmA*`8|Aii2?n=Qd%y$5hwBD$CSJ zX!jKZok(-Xtv;K*T_qu`#L>(=sWdw#lJ0>ju#!A}{wB=(W#N^iB59eXSJCWl#T6EU z3C3qk4ZOI1od_xDJz3qjma4vaYwaax^^yd9{W@nSt7wXPAjno;Vt$ zV23&>7|X%?QF2|liQLYY)*|(tZq3>7p&4Ie+QBZ{cwd&80tT5Svfrkc&j#QF{yCuNHPHsiT*V8!5mljk>ZYlH14Aw%Y>K7xjWJ2argDBv@U zSpdmZa`xy#^v)7-*SDg^pkqZP)g)U!Vetf(ww!??sOJH#5pr=90JW%q_lLDnQyt_R>S5$7yd5(3xNnM=jS zPzM?Ws?YV{Eph!PQLBp|G4cF)HAOO`&{a1sRVUDs5$@;6Gp=ezd zGv!ujw+Fbc0aVMC#FYx#{alI6kv5t|cWLXTxoO=M-Rw})p*sVaq@2ZEe-p_x36COK*UHfSwqG?qY9DyXv*vIfnJFVbv(Vx8h z)PMWcTfX>(Z6cbc`c@Fm3_W(?&J~oLYy1wqqUDDc#2YC$E}AgtC6##>&t{z`tjo%* z@1DmftrOJZ&B`Dc30VJMmT-l}Du{NEAhp1V>s@QBU?*oZQ^|0KhFK}%;e%U@d7`9r zW}Au5BVoD_QyspJM-88{vm+7|BZC>3g)V{G_dYo=r zuGUDAogHbBYMy4;u!T9ilXXu$7o4IsiJnUX{}~VEHbUB6E2ndh50j2G6(A4@?~(~v z^EPo$8o0tY>~@2YIt9^J_c;xxYhvw`dj0?IYQmxp$QncGf9>QcBn$d-&^5ig8V{=u zJ{g~SI|wcMincI@ZEGS*BAeXv4U4+q8|8*;M~zFvUl<5Tma~YHGCEPFV=0*0r5TNd zSTB>d&_vn-bC#ydvY8xT2^ZH~>jSwkTMBKSW=z)_zL?%*2%4ZtjAA;h&BEOJZrvig zD|GV)qH=eaCPEGODijf2yUiejWbwc{|7@k($HAji=im-F*bG$a3N-{cHT>*A-dgQ+ z)4y^=pf_u>>XdPw*W7*^N{yZ&no}>hN!9yQe2;y8lbC&F9X=z zQObxjV>{jzpdH1ls6`1bJoiS*e&rpwe

5AxRGvX=ivRDsms$H$jk`BbccwzkO1{ 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

b%DuZ!5ym6 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ð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 umk3-@t}hSV25V@FBgiX#5 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

O9DWG37YEhrhdp=gyUybdZD42FJ4ARu1W(y0;yX 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

b*y_XaUDTSD7tI~5_E~b z1s1)=MAOq2cOoN(S4B!M>u}d6YcjWQ_HN*_5+k>;TL*c0NF?H_3wYHHynK)8y0S%1 zOuV6i6Zhd4-6-^_6ut&>5?Oh51lbS(Pm`LLyei!_Bax$X0=O8+r6Z|^lduEtU@~lQ9r-cr26+`z?bK1FzPB7waw5-Vtc)h)Z2NY~&4?8Aawm3z zaHJ42ZuS9#86(N(h*&Oh#g*Lr7*j?3s~i4PSB?0yjI=67>Q{(BE5Hs4ywza-vHLM2 zSx_(;MK7UFJ{gmbEW6)5`d7}B#D`HrA!=RH_iUmQlo;2JdfF;iRI zDY`(PuT0x@`_?O`-&m~=9LoiFqypvH=ca4p@7k^`2BoXDKTH`eccXfI(e|Zk{CzSxyu(xd zI_zLPna3wFu`;i`JnzuI_NoIe9j=rNb+`$oQt62+EknH2^wo`^egEQjU?CzC7B^BP zJIdV|^(+>{XerOybz?fRS7|~yfA|XIf6L}V0P)tkJzT@bztUoJ)o4P0s*PbthLW9` zHRp}S-7gOqKHu|h`n-Jnu`M$+#-nqF(#hG$0>tzB#rf8?j}2xe-~P2~s=plHz5gSM zgTa{f)Mtv$nnP29sW0qM<3|`ERLmLun(Ht15@h1H-qirB#9vNk1@_AHPhTZ&=Omx> z=(nre>TW5$SL`_XO8I=<{PrK8+8-$$6yyd5MgV;)t%gRy_qp~$L$~5b|AHF>Oo9tY zPi2I)f8Of-+}e8m&_9Us4;p`&%9Eg*06OL*_N9@o1d6stWowO%j365z!fym&$|C}E zL^uFqhl_A&K=hH6t@^T;d|uo3A4L$GMl5(3{zr3|2^44qY3 zKEWw;t!98u>8li4k<^Kw--{#P$QjZ}8&}v-YG~+L7l%|HbS;emnhMByC(pN0&(ujH3YNhXCmdgydm|e=0C)5`2;jJTZo>7KI}7Pym3o z?CKth*a4g_%#flWl=@lf161V1oKvWk}X-VQs87~sUWOIbq6lv5>-J{=v4 zZ!Qa~n-!FdfqTgyNC9~9o7P9ovz45Y%{7PoOT*`o)*&Pm5LX5QctmsYV|!A`x3*S7 zH}vw8u0YtvF@SD&=XggTqMt;V%!(MW4N^m|OztJX$+v{F0aSM7y=s&yqUd-Hbz%m> zGD8qYSlb%8KRl5pm);;8Eg1-7^Pt3EMXzdiR#v2!wj1WWTB!3L*uoqM0ulIaBAF~E zFQ#3sxh#UcXPZzBKZZcCS*Q@qi`T!rAwy!M%(SaZ(k_Hp+6v$IL^j#gRw}>==Op4F zc$dk)FVp(`?n{bt>8v+B^Jq2hp(LkMwZJ_CVSp1m3$=L^z#l2S z^kaBJ<4>`TvbNx-gz>+p_y2l0*XX{Jd5v+e?fUlOzskbe6^FZl?+mv}3s}0Z>C#lc zhcpShnAGhZGZt$(K{y=7MHcR8^#y`#+rX2_YS>}Pp$7pMK>Xx zseMyw=cfDYdh=rYe#P<$^?z-cX6jIC*qy6qd_;YH)VtJGTJ4)2P2BFE*Y9nAB=Egz zov&!D3~aljI{eDNMMNs6rvoG;t=AmK+B6L}^pQvwI($H9t}rsy|5U1_w_Du01KP2$ zq&yFHe`-gIsS~_8$=B#6+;zKek#$VdqpDM-#4BJKk$7R~mzMHN@b*wGelAIj+iECvRI7X?JO>j>v)>SGk&y)vAPyOYYHeA zv?ccsq?!v%T}Z5xppFyoSPlpF&$#uP44-2fz%RqcOFVx_^ZuZ=O0nB92j4s~c5%c> z>7lc;#pm_h0`OV313@Slyl0?u3Gh_d9B{P zy?6`KwiIqv0GEZrbdywP{apajSn3;f-(i=@@XJYZQvuw{VJZQFab_0+k=lFrV@&T~r;NMNkLX02Cf_vIEOqZPY#YoTr5R0| zO{@9Aj-ElAafkUihu`-0hGmfJHs2lFdIg1|wX_q%VqXLox(Xd7i3$M?k=oiwS?rK8 z&uIow-EQ6Ibj|qkD+Eg-!ckX8@S{%nRag z;5NGObfc=y*od6Bsd5h`@Ys)4CDK3&f<5j(i@O)BvZHZtDqD`Qkr zwX&WLKs&|x8**Xx;5Gl#EbweVnDQ8l4BUdd@=@QrKaX6kc^7WqcoJ8K{->;ys)gc_ zQT>;rf43^}ELJ^6#Nj9|_k+ePd{%Gdr%uq$RO%Y&MfF3mW!O{3WHf4%X8cvu)YI`r z7D**fYSobi`Gy_|emQEU@&aRzNSUmyUvjno7TLxk9oovIuI(zcdaJ5v@b3HFHLy0O z6kmQ&vS-7;$e8SiKxVRWlfBqB#Q>2cC20f=Wd>USq{X5F$>&d~eOkQ{-@yxOp4KCB1P!u7yCcnbksh1 zuBQ7@ZSxb)y@HmT0agH8)>n4Yo=d9}XZQX8SYgr)>n6^=Lbrb~%w>=Rm_%O7^s zWY8_vS%{CMHJv=lU=X&^&;NS=5i|dmf2;h9KNZ@>h!rZ05KB5uHrq+tKgNu#FTTG` zFLFEGardLX3Zo?}KcHiF;Q1rtec$HXnLH4o8ANLFz8WJDM&PoH{X;H(4R*kb!oi5$ zEd1zbsElV9M%EB^JUz&rhKZ$4{2&TfieKOl-;JUbAQKOuuyx$YmjtL?cj)@g@$u=V zewyl8&lvG=VwH)Pg+;WqtjtlvpX&A3TX8uofL@cLvy?2@#`+OtuoDw#vHqrKbZ;a5 zuw(yF40v)6g5Lx34n`&7H_~RrB5%txN53JlB@EqTdHk307duO@Y{;|b-g}>+qSsG; z;l`aU>2eVKY< zgZl-9f*|!^(lT-RoJRqWk0GXd;~M+>*9q2$Fkp%BSx9HzU6~$A?K*Ys>8J0zUxmb1 zbLm`Bn#(Y`KctEMw_tvgx9Sk_B`=|GGd-p8RxBHg3nMXA`|SHD3;oEf+hl$qdSUo7 zZ;53+Z1g8&W+X1<%Lz)g+W%eqEYdeRk@YrzqgKGXi#mQFNH=kEN32&TbAk@|Fh=j} zl^sDT;Gj&zrGVFNkwqud!_?{P!+h;@LGT~L_PEJM$oU%^Xoft4B)g$I&_G+(4L$Pz2sNyyK2L_YQW}*N?^#9XfEs=!Tu0 zoeLK)hKFGgfqH{sayV_HW#{ z@e@DtGvE9D-}}d}{KJPIdhqDzh_9{#H|z|7Qs;)SsLr8JyE3Qs1Oo5F12_|fq8A{L z1MmPobV5(z-7A`=iKa@19HQUQ_2ZLI`INu*-+$}}U-5^(3l4$&AN>By|M-vomW*gl4SkEt zLMacoJU3Q$w5i4BY0QRqLEbdmqjWW1n!p8)T3%i50_<*e&{!NRF`jp=Ub4U?z1{XE zkO+Is;Wx|8)oFq7IF*=@>@d4Dr)ix~#FCJiwy@Z=@RG=?%x2O*8_vy6>J-%<14#Rl zNihGtJbAQ-^lLivpzUCCi7@p0oI#It%_aXlEyz=oa#I6%0N~oaT;rBWX)Hda;K#yE z`Jwe?jY8l}A$};SQ4JIT#w3>fVsCmVT&0DZ?2NSHg}#{#Df(G}x=?@#G%3$s5>7#Z zsAn^1bpnt=aMSn-+AMNY*AVBhBC!^WkgEycLW^o##i44#tVL>-(mQQ=y<9dfQYFG$ z_LvM)2I#7>O;yVC^B7_vgs?T|MJZ5h^q_jZIxtO3q%BD9rYppim(>nfX|E(H>xe3f zivUYcsKuYYV06~26T0QA_Dv_H;Cw+_7-ymkbI6vKU2`u3XmLrSNF4~OH-`gQo5#6dg9!B_tGo<_zzatVh9~ggiQ?-(Ktz6XGQoyU4v#NhJouc?_^coM$sd_EYft15 z4-O8dY4YCF#y{%rdp_^;zTo8KaC7W;b{!CrpAa3JBZN#E3HSEL-}?1G_^Pk_%2)is zZ@R&a!-$AGJA0v&;Jqh5fpg?N5qa_mls@>K%nAa5Cr?Cx9tRwNTzH)f01?r-5uD@9 z^WMjadMd}Mj6;)$P}y-v5NkT*({nNDksc!Q6YTB|S01|Z&%Wzl{_}tJPi`Du+uhx9 zZrt13%PSC~8y~;!1Q&o=FqZekQx8=wD2uY9>DKaL~f2!H?{2shL8n0p@o1)u-9;L%T0c>gR+JV2S&Q{aR@fw$lH z_Gy}ke0Y4|!uj(EKpwy&P7c7KBLF}7_3r-a;w_iZIq#=1^>@ANUEcf2Z`PYla+%~D zp4;0Wz^x}5CKnju!PDdsaa`>b3mjPN01?8Mvxq#93yXx#VYF!w9TEW`IE0h+@n+f# zZdk9^S0B2vIhowxa6ll0P#{ZsQ(S10{>1ehkY<%B`dyCL13Yywkwz73hRF3Rqfj|o z;8I>t_D>qm?nT#Tc<$R&1;u>GWZXUvp7Y9*0r_C;scRhl zflVP{fjS7z)~mm5Ufr2jr_Q29OXrL^VhnMJ!S+10{BujL&PPoue9$V<5oHN&uDGd{ zsL!;8t%#=21@stRe2#{g5&Amxh`vBqv8ox_q~h=2*vh@w)xChwTirQIac z=993)SP-(4Wg?L1%LznDgAfS9gDQRk?>(eXhz4k(vH}Dr$N1dme!wSu)F&PtA5VVT zOq2I84kH2}x{rR*$G!jiKl|zAmieBQ^2O+*y7webK%VPoVeyah$%eW)01ruD=MDB%<2K!gJkZT`c5{Jwwq z9p8WsE?v63ySukuuQ!`D0*=GDJMONA-M}Ll5yxRTyyjo}u`hYjlb%il!?0SdhQT>RBoBV$zxeaM>Pb(1 z%<*9^E>#xG!zLE=2t0Z3DMw%@>!a}M9I$V91Hv%4ad4sN1_0q!C@KQ7VQOowkM;8{D_Hx#7% z7<5$Gq`^SJVHBMg@>ch<%3@G%CosS{#7*%^{LjpJEP9rH32U~<*u7fFl{&Ib zmHjngxr4sLpax=+NMt>2g7U2lR_%wXOlWL?k#*ki)|rrPln~i_K$XGD9U$FoX$`I(w^ zLjG-lnvw*8+B`?LWp5ikGd33Stwmfyw%M&35}Zk$m8CSg5dtSDMMv6X_ic;57a=CA z%wy$LOpz1xDimO~s+`+rtg78Ng>kkf#i%lJDDfx@CbvrwZ~n|pZ9OV`P_{A%z)}!K z2}mUh07%&M`~{`aBc*VKXv$ZToTRa=T*W59)zh+0CS7?qkGS{*2z~+@JrN+e1S80^ z_XN`fC&wrId*?s%Z+!00{jZ-~PbUQA9Js;xV}JXtw}0+uf6+7@uTN;)LGl3L;%hDl zK;Uo;J2a2OFwyaU{NDfcn%Doqg$ozf>-FYjb8i3K2R`pZp7P|UoV(>u+ zJpMQT_FupA!TSd{5W(OEbRz+T$2IbU6=50t-G9%oq2}noO2#vXUDz%b#MHa|L&iU<2X$oU`^iN`KU*K z$OpaPagTetmCXzhy!CBwAxG%I`w2%Na()AT@&NFTx4+}~_}EXesut5&4?E-T z=!PKn!!WGZ>yP`GkN?!a_UW5xeROzZeX>DE1PE^2-C3>15nzG|Xzd<#$33@RzPQY64>uEOSSp~E9Q8DpKx47k@6jO#FiGm7#-f*L)hj|xZS|2P;)_dbq zp6PGK)7Y$VNfSOL=PE2pQL|TLXVK}%q~Ohq&@C06E>|>1b>biHG&@&$tAjP+{=#rD zWXPbZK*$ca-d9tH8^x__4paRAj#=mGM~vor^{Zg5uv}155t({O;9@Pdlz)L&pRDdjRThGQ4OcW zt>Vg(*)1uUGVy3So^9){TI6QM)rj3?D6X!%20Qhp#Z4R)k(tc?-a4$Kbwy%kpO!d# zGmW5O7Eg_UDC;~2+On^6z|J~pZqFcBj?A9rQEF@U-LV~*>YOlZQsH~hzzB5A1)XoF zm|lqU|CY@spw2j6xc*^=&&6REe+xnX4+b;Sb(AWq+dS* zGn;)h4sh|8=|K*4N{c2~02tRFANwI6^ubpiy1H7e zkeqXf;G9DsbmaWxhXI`fbZ%#Nxc^=6{_&rA+3wD+C-R$VXSMq|pY=uG^3DJ7;^jTU zX`1|MwW7`R<3I7^4?Os;VKoB4=$so|DAy2qtnDaGd1evq(EY=Nvj92fy=szjN<9 z-iAn?j`0{S+;Z_Be8WHfoX`Hu@t|=1mw)b;|L3p&FMoW3g9Dgcd{zf@J;gbP4uL3s zY(0Fii7`LuUMam15s~+v3!-x_J$!|L?&xs(w7>RgKk~94c=fCQ&<)^$0C1Yl4sO`l9X-POWc?w}fBv{L-f{b*?z!`>`|i6pti`w* zf9rRC^Xm2Mmo8nncI^gD;HQDe-EzypJKz4$Z~mX(I5{~+hv5~o@SU>tAzi+B>G6+$ zpVzh1`ivaS* z&S!5e3(q{8Nky+M3L8W|Uk5Oq>daPQ!>PjzsGNE2lD65?Z$U6t<=u6lUAY*ukXP{v zk?P8hjnAyLovJMg4poAX6-9=ijHI&{^;ns*0My5)!O|WZgTOOqXG>eV1RI##k%1*j zszvUUSyj88E{3<-TnWNJZ7RdKZBj9^YY6S(@Y6)d0DJnqsrKF#mFgXu^i=XpRkDxcn^Le@54UDi4O#TIJ$v+ zcd{KL2yR1l@Xl3<%D9L~@7&hUZ(H1HjQa z=U^Pr4e-DNR~~ra?Fbn5iMep`(r0|yXB^z(?!EW^x7~aHyY7GY$>Gtj{NiuC=5?=i zgCk}X0APqh7V973frv4@ZsnqFha@*HS~Da_9y(P~548i0HR}_t*aHjc+=C z%ihlJxO*_3ySVr2SN+NN{kQMqB27TyHkP0KpTFa+p;$zI^5mcN#P|J_zy8 zzIOG9Cc1I`@PGZrudPqkIJ!WpbLcX86bd=$oEu_uTE>&0mZF4KYUf-OP>-X-lVf=7 z;NPyZYsJiKvqd~|$#badzK_k7|@UrHVjCjcT3hzM!lp8(dOi3eEaza-u2*je%E*0_l`dwSEF+y zc-Y<9`wJiOqG1?szx~!{Kl=mRFl;uP&1P~q{MK*(=70aeAM&t1ck#l`{@(8H?*93` z&F19aeb2x9-QWGKz?JY65_)ii$A{~4x9onzM|{LMjGh1i_xJZ-_Oc)T(I5NC8;2)H zM<)+Ia{bDc>qm#j>yzpCe&AXw7CZ55bfnR**#yp?!_n1XX!vd~nN$3P+}AEqaF5cA8aB3z42kK8XO8AJ1M1 zvgpNb(D$$wJ$YKi@zr2Uqq@k{hmR~v6u!7MgjC>R@>BCapG4~mq$Sd3e!sI`dYM#& zFhilf)CX9N%F$i&!Y$geAQH@!M>F zJ$-StWk!^Kl_m(_$|7dJI>r9S?Erz$!v~;!v zxSb?SfpFFwB*E3pTe?>Ku5Is9rw$0!dzRy!*LU?PQ;SR#xhhegvGaTj0-1#*b2wGE zM9GKHKO$|V4|ZA<8!HK~}pu?KMa-1$}A zTEiJ_J!xdJaE9epHB)i9#+S`PmHlM3pj_Ai(TgpdZ8K~(^Cy7BN~A=xx^>ocsJzQU z>x7B!oC#va5xx!u-lO*r{sShEffoocjw1je5;{+A+N`H%KJ%%6^9#P}v5$GuGoJpO zXFTl#KJjBe`MJ+|&e8SF?#>FqAvqvdH4Fs|zyWat0P>zNoj*Q4eBGbEW}4{I#f!IJ zy8VvZZhhVB{_Jmk!{5Gfcx~hTdcEFEn~k4Bh8p%{8Ag|%XF$NPk&ANd-5Cs;1B-j<=^nlf9v=D;N{oX?>_Wbf9H39|8M=>uYLI| zes^bQXB;#|M%UGz1>e69Erdqrkz(Q?f2wi7*~W74s>{Ye9z+^{g+?*mj)c1 z8+LaO&K;b4Sj80h$knSuoD$H3ugwHk(DeX{1mFCvU;nTE#eaCy>)&$Y+TryZS6}*o&kc6U}M$Jc-97k}l8zT~ri^kqNzE&u5M`G?>9kKXvFZyv`L!To8QcaOGV3N1eoQ;X z$ppEQ0g>ERlVPP3KB^*sgH65jc{y#17)?CKt^-{i_lmJ*(^F{m%qvbV3$R!+V&jlQ^y+e_M#fm+^L5%^hjvyNu7Sw4OI*^#B2(w4$xm7P+xl1=2@4G)t+ zHqK*)WJSe2C>VazST03PPYP2Opwd!V@iClIj|S$@6Xoq%ouWiG0@I&@P!XBWiGp99 z9*I6h88n@#)>HE5)*RRgGW+LOcv%668j*o6SZe*Of1ofl8kBCc(vC&?43iQ4UYZ3c zxsVxiDy6Z}tXrm&FKf;4BD;DnP`#N|&!V!e%_`x7yE?70MDcALQqpCTnjwnL27KiV zFvTN9#pV{0dlXHf4kAT0fgjBJY!i+t;T&MJp+emt3byu#arY|$!J@~U3nPq|GP0p1 zb2uXkSopNmlqKN2fZUAvETk>Skzm81r6%QPJNOmxk?8t!GgfrNd_Mw&r)Ah33KU(- zo3d2@f%HBNf1!g?^2Orv9O44-a*b8+WS5j+ik?)6h?}<%)=v^gwc?rr1P%ZlAON`Z z@XNSbMdyfs;5KVNxP!m<_rB?Gf9>BJ&#hL&2%a`4N1K!IouYU@Cfs^;h~yCwaY+2| zaQU{2x8L@tx4rF6!?-iJ;p){Z|LEJk>H4*+&v@$l?d|Wr=GCwN&hPs6*Z;|@hH>Z`s}5MTg)K<0l@_Po6vsJHuUf-VFdq zIE)Sm|Kv~K@b~`S*M82Yf8pbw`1r$Xhd=fcFZ+-G>AR0NM{aaPgh}&!*3Ile5hJNM zgkKJ0{eXPdS4@HuI#e&dJ9L994`U&?qeFV1CqChaf9yv*!7zYB00-;!nl=+U^c(U7 zNv5O4@SYGL*1b0Vi_GOTh3j3_}EXw3qSgWFaDU1|Ia`8 zeK1YKFrb6O!_C+Ko&W2<{MYY&>XV*zGWl1&^5t)S>z}U1l^b!|OazHPZt#;Q!0Qhk zKlAC&`1HT}+5i0Ce*4k!(J+o~HNNf7-|@v?_}MRh@k^feV`j>zC7f+_+ z{euIC_%Hv>KmYAt`#=Bf2Y&kTPrUv3cq&0%%&3Tz!Y1nzWHy_G`hc=JLwrh%8I?Gm zROtsn?CHadZ!yM}_tZc6g5`5$bgo;T5P!m`Z~SwJ2gPNO7+444*;o+=BA}Xc)A3=I zbJnj>_vU%F4&1GW^n`UeI=2Kj_}il zUZ>U^fdnG#H@08S6r&c(Q>&Y{6w6w*uA17m2_W@yX2(l)@V5qbmU%lIdW~iY633_V zJq95Opq{T#-l{__xcQg%!KMWOJ1RB23n%8i*<@Am%y0Zw5LlUp7w-x`*}#Q&Bw9%8 z6I?GOaMl?`=XAXFB6KZ{4qX<#S#1dFlDD`+PYrEObJ1c1zY1m?mWEPD-56$s)&_C~ zVucaQcg)!wFehw?JcU}mN>!9De`aAQDxVx(saR3`!WR)Bx?w=XVKurFHw@#>-X4q& zaX=WsBLGZm@_yr1IGvnKa0GmFJ3GfG#}0=KVP|(0p3VVqANoNb`uN8^@vZm1d3Uw%+yI__?&p5)=YQts?zr>L z!|T`I{m8pl;|d&(esB&Qy2*Lx(0lUX1L_`tAiSbBQ7FyC?|g+<)seshkOy!GJ_rdp zbPzVvVXvDF<2Vi@l8f0L0uUXaOeg+$7y;L*SVRuc0U}LEL^u+~?}8!#5cseWlHUNr zcyxmv`}n*5(Kr2*Fa4S?xcBXEUhS-QcXn>M<@{#tfAXh)BrL?i{=ub7mpst=AzgA3!=e%;r-;*bCE5C7=*_7C=Vc6NYZ9LLwc;dQTm z?JEIbcW3wD{Q1M{*Ju+<4q-;CaYRHS^nSQ{<;K^2?brVPAO7zD`t@Iglau}Z{ew#v z{bu^fpMKd-z{>!@4a50^b31Nlz1ghS>)qYG+i$yLXBEC^jzpe3kdF+HB9f{p=_fCn zUGDefmft4K(2?1cP#9|3wP=^Ze?HX%bstc6Azov2m`+<4 z@zeR>yo5n5XGGlBoIs~&VwLzi_W(}a1P!N4z1mIt<%&h8nuHuvbGygxB95~_XNlO7 zbje$>zByR$Lbh*%u~E9Xv%Kk8=Ey@<00961Nkl%Ws;Kzh_~J-J#pG8 z^<}rM{agUT8>L6Lktr>4s!+Y>m$|^JMOa>ymT>ZMgHbwF%ZSQc8k24PZ0KPDT~7k78d}jO;QD) z>YZX3C3-zffnG9SC6Wy<-jE={5FzOtpvGcPWPvFFrgZT{7pA3o&AEWmf+R&nYt!En z@3Q{fgbKdRd&ip8s#q{9w}KLeH0<0}Sx(N4SKaOqRg2!J>^N26amzZqy6!Hzm0$6d&w0iNo*W;o*Bd|>SL3)E38w4UuLf%Fy7TUf2j{(?0LV|% zG)+^ImOzZ(tiAV>_nT?s5Wzv9&kdOBKQ4R*W^7Q?CM#uBYIWBgcR9jH{4`CI2ZZ=_a);~n+I#PTyr05X zy`$So-)N8jRU4f^*u^lstQ0k9^26w00B~VfuOP7w{V=WFw83eEeuI7kevSSF{RaF* ze)4|ue%knHGfh*1hSM6RH6preSEi4B@e9A}JO161pY*J0h!c{KKZ-8X#8;sA9`C-JJ+IE5zJeHKZF5xrZ9ARDAs7C1(Z!M)hZ*XkEC~>u#e%iby56JmKfso|Z1jt|&>ktD@F?WlB zkdRWMM5sodPiAjlH)pxg88y2i7U^}WVoIa112sl57qe#$N5!PsZ$r`aPpHMdH9^f= z%^6`sG#0@D8QjmXmxamkX9A?*vg=SILP!N5nGz)%rHTZ`-a4^soqNhlB>HG%k|O}+ zeoBWE3Kz95wa7IMWE-JsZN1$B2D9u1tRO1=ogr=+@g{T3ksU!4E-m$kn#@WAXQ}I< zv!>tdeda|udu!>V?AG-kXjN)jD7GDb6I-ytsqonA#*Pi;l|J~ijn_P|E}Xp=S}6Y1 zA$7hKCz{BtE^dp6vxps|#+zbnjiS=fOystdL>wAAu_Cy-OVPDxWxG>A%=r*^U{Foj zio}>*dSnH%fMD*Ft4pDV;M_1c=fH0^tJR+K ztKDJmVCUR0jN{-2ht7c;2nRw(&cQIc!8r#G!G))I0KfyFLr?zj+VvIg?GF1p!~XgG z^E+-Q(Tm5&@RX-M_Iv;RfBvB7e%S8nU|Rdldb3$?rfDLgz1{r}dfp5E!*_rG^Pl^I zo#9|L?5)P#z3~A3FgREZZd@S{I=7nE8^4)W!_GMDf`eg&!{EZlszTB;*qCxFMJ8;4h~ zUOPHInx+W}FW++e7k%+p{KIeg_FFGK%F)gMtG(5}qmjJF0fqtnW;(ofw7c3JVcZ?} z_jeCsNexr&EdB|Fp7@^|5FPkw0`GQr_jY&pc6WCNw>yqQ90w`z_ehh2$-!oTwWBpm z8<;jQO*Bp3dp~Walfxqit8rWnZdi@0ao9%~{Mz}A^W-MND_2iG=`Vl$%l`XMebMKC z`4b-Vz60(Y9v)r0dhP0!tJkkyKfG}R$lZSVju*e=lYaaMe&#E`;tSR%o5`P`Bl3Pg z9GtuQ(B?0E^z(l3KmX`UU-FkOUbxk->F~zkwX4@3dF0CV8#id8N8f(WKlrBa_{P8c zO^>>C=gzpZ;|^BCYVt#fbv%(9;p#){OShhX*$@Bp*Z!@)^ThXg+5kI;H;x{;a^?E9 z>sPN_z4FMFlcSS!2j}1SeV+XFU-yka@cln{`L=V9Tt7neL=+#@NqWUJsZyw5Bc(;r zRue-Un>8-icv60>hm$~=LKf|?%RNm@Wgyk8&^4@h2(KNt#+6}4oPVVFri3vi7(2TY zoF!;m1g9YBjI)S^JW;J{Bbp0ocpprDY3qd(KHCm=ReIS2$ZAp+sa2mX=v)w?8S%$5 zETnFm=`L_O_GYpfZB@ec-cK3&m81Vpnsv*TC)NG6DH_GPolaYGDq%_)#X!xFEaw_@3CECr@)4zP@^Gfk z0D$odZmHmcwHzESfbqJB>MT&|R#%ACu0!=qp}~O)i>VlP@9K)KD05u>QgIh{yCrIQ z;oJn(!p_|=r=L=8D80h&C~wv7xr97sQjpCO93g;K?VSZA&y1yX-@ zoz|!yi4GhEQT09b9wNPhO$reOn}C^3rWKK0hTYMvcsXV{V@!^maB~j%VW9X}KpI`L z$zo0FWD%?KjApRmmf{Wn`dq@uCsbY~NohY(+9`y9*PjsGwDE)6`NU89E6;f5GY`%k zOegDu)!wt8^~`Al!)ic4B68s+Ap%Id4+S`sB8Byu27(XyuowLO^WQw~jxd2gK6%QM zpLuc!L=GmJ08Xw>&wc(ge)cDS@jw38|M+Xa`aj?NmN&0Yj#h5l-N z2Twq_nfz*Z=dXXpXFc!vAGBJHoAr8sckdZbf7behh>*z91lO-jFMa7pKmUV2_=kS_ zhkxZ)f9cI{c+*7dgS~^tJ@$S6+F$w9Px_?43~=(UhaUXI``?ADVRPg7&b#is^R7qv zH6Y^2@igw>-}?Hm`p6f335qNs^op*h~7k|;w@u3@>Kk>UetH(X|p7jZfQiuSM zkkG||2^|dRrVZTvxO=|pYrc9tt$|>3a(wHhJ0A7u+r1|@y7C<*fbe)1C1D8`A+A0U zIe6Cdp7&*6{#C<{n>N$QjiYD3-*f$h-UAWR1n39>A9~>E$xnaWzx(d*`u$gY(J%ed zFTU!PuXyvD-+JxZm0?^x`q7Vi*0Y}d(vSOu5Bty$-`#_&kDLG@4h}*d>k$AP;v)}k zp8Y{je%X)w@X!9wU;c%k|LNDh_B9XQ|1Jmkm?u2`nNNGpC%*JAKmP^KdCQ;O`-NZl z6*o?Cr3_BO5V+8IHD+)!7hBUR4oqt1zU4_T`aepKL}56vbK= z7<0%i!H5G>7yDo zi`ra4E2rQgbk1FxMobcKl|`_AB+8^gkgAhbbCN%j<8>z=@RGf8pKpx;5WWhb()QCgQGa21 zn0~2MZey7TiFtA#mRGTsBS=^>gwUs@cui%qTIrme{5;JHZcr4%<Le4U)Fu)J=!Jj9|(US6VXdnwyW9E-&lYTFfbc zR98V(>neM@WeWm;;2d6m$p6_N9ACcOUAl9%UQd`YTCZ;LEU>;1Y?JAI3yGNCk4lET zly#JkE`#+u(Tx>lvrhpq1y>-5;XhOKGOY06!fI!~TspcAC&v>_;l&?-^f*zxn-;e8 zC=v$*Cvbe-BY|7N{yE1#l}QsEUh{tPfJ7er2K)qow0mw`?Ktv) z4*UjgTs>JIO%%64MnE_@S??aWi?+1-Oj9zG%u7cTD&BWDF| zjwe6>2h+wA(#4CrIKtsIxPJX`b8>QUaQ@u+0SF#^*L8H`rOUgk9UugBuvx?LjcK!{ zaloDZVYLsu9bc!DV;?sS4$p-9!qDJdJrRzu+I9N}t6_xreXb2$dt`HRdh5dB0tOg!3T|=fh1dum31|BSMt2qeV=q+KD@=K-onWA_Y!gnA z{DL~1_T+bWR}a49IN?o=$m!^#QIdvjAuN=;BwkKuRoT5p{}psQPH6t&?UrZD!VH zXMSmUJlyJO7H#{I6YcvpiHS)DpR8#XY!u7#BV90RwN515Z;NCS0OEHv;$69Te0w#CJ3n>CM<$vo{^WKLLS&`Y|Z_WAW$!E(tmIYbR zTBcl=7AP5@6s58FoO?26wm3KejW>+EX({6BIUl9q`a6uTc1D ziEqn^);gAP7LxM_H5=JFbLSC5e+)~WdTn88hGXoAqAmvY<+hbS^OIr;wo(K!R?mXM zy&zuN%c#{Ev#D||4|-ZUn1jI6C_<}S-wW!})k4F~2@h9HA6)e_oSdYhgIVh_=jyCS z$@xG>PG$F0Hs~z4iEegvr0Zo#M#$k(gpel{epVjH4S4-w|7Wi_zI?m8^yqQD@d$Z} zOir`eW~ClVf}Y=Vs`gQ~SeOiIPP(d#tXoDL64PppGm=Cf0`quffy8O0b@exq5P&A~ z1QVh|1n1DX!C?+T)1nhm&Y9z1qO`=G!24-98E(`si#K<}r`#>Y3R5XW&GoO6hPG;O9S>@S0W5I!Om9wq|vFioC3O_N`5 zrfHf2O%BoFfaqL^O(^U&K%O?6%`~iFwX+&m4iISCOcSkEBOp!Q`-#Fv#+bg<9t$A= zfUsFlK;(vD7%=Qvb9}TRLgxk`Sf8v30g;@;VdchgH8^ww5_uvYDpTYE5ga-q7Z*Bl zwK${r?oZgB#d{x_jQ}ouI-U>!LotYnOq9j6WZ5PI0eJG>PeDM?;SfHL4uFVmKt#X+ z-AEYrVkSRrXj~2BFrY&s@|$V1@d)6?!8rgzA|y}jx$FGhGV&Y0nY{OYT#ci1fUr5) ztWP|GL+7TApQgzZq5~Ii@DDCWkO7nf7nTDEk#={7aWw+Mw3#;R2>^z{xgqXCMQPue zYU0Ui)iDI+Z)4?$U~=V08&w3ULB*_O3sY8r>1P6vLh49C6^oXmKojPhj)Dwe9yOMA z-hOiBfz1;?VDH@J!F%3~C{N7@(5M~RiKtCLKz(bCNZRmD@Y5=eXgYz#6?W+)E>>oI z8ltDvh%FbA(P^D1MO-w~>^4|nt8W5t(!)e6`n;-xHL5k?j%(J@f{dlx!HiKa2z~gp z8Et5BP=^``)2xVXFn4C6Qe5N-U1$RQ5nIIhAw zYBUax;!Zsn3Kl6ji!=>x00f$7z42j!CQ4=h4U5|E?U5H!by!&t4#(vgf@-qQyltd|>{b8grH zM4UE0@L(9+IJ!_6qbV0qm@IMn!Ho_*4lu#-$%Ft1!)h2pWpHqVb3=#`!RL~Fq%&4R z5CCu-hhd0Pwm#mVLqxaQ8A7FsLXj%oc&uY=WNQYzWlg9zz!IvIhqugFijf#p%MPdF zYl2w@rRqzH7Q6dA2Dq_K6}raNLmgrSg%!pqIqnKxjiJz=CRYX9^#tQFsm!Vsv~b15qyEz??so4=ub_MpuO zp|`1Cl~Dm&`zofOKfZackS?y5NfrfZRY`Jh$hL@Dlo^n>O#|DIp%F%IwKQ8z+$^9F z5!XA>nI|~{RyM>lnGOJ;wBeeD<&=v$i#=8sOlwE5MeUWHZgmd`Oq#b^O%%V3q?Djk zZ|Y#4$>t)Q^#sQZ)Ouj20L`PKn)kQI$#H2DVD(|kzL~HKI&HbE=x#s+L0pVSV`|I0 zL0+DA9kQd2odP`nS-NF!4b@V0yk>>2L2BQ@Q?3)ro~?-qqg?y8;>wYC>DQt8;Cu}@ zAKC?Pah8*qD^jMh<5TFYR4$M&EJX|6HksI_r_*0hB{6CX$XKe2>zu+WU^W5>%!Ox; zL9Li!oja9TV+!o2997Sm6URZfq95~8pOQv@O0G@w71;{30?^26&N`?kTNB*mTVl9P zWJ5nHRTP5zcKJ&b3)*Sosx2eHzat)ClNwM+$}P+;4rgRC~6o` zVQBM}l9FXvml-LWy0(D{Q$&-u0ZOlirO}Zb7bD(6SDtOa1%SD{5dR-O{#7WaToZ{B zmSA8|;81UIKw;Nqh}(-~+M}f`%*c?VP^YgQv6sRC(K$y%6ra$+{K;v6>f?2Qh|YzL zhZ$$`J1LRO6oDpH04Rk6=Nti%2O#oPy}iyJPXYk)1nA-%iBiQNztoofQQ2t~DZLa! zrFIi?n!Lcs#GsT5tHDvAlJvwXQCj;zPmIt6fN2^+w$j!2h;Dm z9;W)|Gl-QbKpsuCJQ|{6qIOhPT1!kY#MDY3aZ@5e)C4SSIfCksGHW_K`pSl-&z3wi zLC9B&Q_l4=&~XT(A#}>=gG9%Wmg{x4lE*fEZX={g72@7Nu!O4BDq)4PpgIK60dq5? zml#kF*MZP-`ulV54Np(Fmp(d+v;zJt7%dJI7jES&vwZ8M!?R3YCid)nI^d>wNn#Rh z>at9y(`v$W9F^wFsO9EdQe`DLb7#W90wYAS8rl{uxSVW@pu`ihWBAj+!&StfC$U3_ zEC7tmp=;#@0ZUF(ZOKzSi4BZhU>$TGwH-rQP#8eyw6>1i+XW{vu=A6lK6UF2Cv^#J%Ud6)Ug9&iO4Srw#5Aj(CKa;vRS`1~m1z*9It$x+Bp*;UD%MPLge=12%#&Cwp!|{! zB7jd@sl?Ju4W1R63TvyNgGx0=EJ!NVq}9PhQ2v$3h>!|o0zg)xD8maEDE_6mQFbbS zWva0r5?NYm(YcHkgN2Bh2a1U_PvHt@J{715V0rN{%`0&nvL!5aK+M!a7P@k&6#0Vm zCizV-i&gVtY|FD@$GRnv$}j4s6-R1#$#*m7cc$Q^UWx zVdBDAOL`XTQa*2MX&pK)h_Wh4F(c2df;2rxt`T-bVNJu<5tDnW-eePOrizK?e9(#a z`XryRiJL0nlt`2e3Pp{fZtu}d2P}hgbHH|=*6LrCU4c8jN(keXoDuUmP`K-q!Wy%g zwzX@+Mji{K5_~u#6^1KaiXs#?5@BGbp>)<`b4bMtenP4$<>v@<@AQ)eXi$PsEy|WY zx0J2Yw6+x>v%jL>2HWi^2Qo6E&u1A%@xFQaPc4l!I1@ZhTCo^ke=bF(Krh;X#W!Z!QmV zRH1cxl7$&=ie(4_<;BGE5s7CDII9xAdVW#@ayU=r=65KDCl*J_5V~9%y3#(EJKJ=5P}ek$yfl?$g_}!gDkXp@`*UC#uuEqloeSMYYmj@ z6ZIZ?X{Lc`SeRMrB#G{{I3?st@>Zm!j%6E@q*T&D<02nlA2a>Mu=P)Va)_@~Er+Sz zL#0qtc_G4jNf}9~=r6`0b;xJ9I#UuwVtnLuWm^`E43_TvA#hC6tjuegR0?Y-k6?;V zY;{mDlgE>3#{6;c1U)l_(q5KP9H?Shc%rC0Q5bsp2@h|qcXnu8gfy(IU8y|j1efKDG90*iey@o$;ePpy`&?Y?4p2{MYH|jxx;}5qFmsr z3^0owa->Nnj833Z>rWYAb*Z2Lhr z@Dzt~s?n*_wd&#v%2lUaEN$s#`)`FMQro}3RLzL%aSl}}dD$v*s&a+0;hLC23A0+E zSc4Lywd+dKFjJz5h`8ww!Oiw7RfRE~ep-DbAYmRfRDPPS75&kXFc3g^S12qU3hq;0@kn~91=))oU+qaRROjdkn0?C- z*n=;r77^&B3_dSzEopfP()@DGpl#6j)B`)PVpb>SLLK z2&1EHviuSy+la;$9v}G|H#U#HXJ@tJDiD~E>WMWSoZzI{M{+BB{DIx|5=cLB4JA)hu>J$38Vp%z7ko#CRVdFROp2BOo>y%OdQkbZftbEY*mTv$kD1fEZ{`!1Kv8JrDDI{UmX zhpkMmVH%eMvrl4rp_+$>l}k>a7f{G!ryRD-Q-`9yGgf$byj;V`Twq0Kl>V{IlMG_J zt;NNvHlOHdc@YiJn27@3gV5eHC7;SYTg128Y`L97&_z(wSmzGd=+mJDVhyxN1V66{ zo}C>LnG2;&m|>n+=hkvp)-aLIVfC5DZ}kwBXbPA*EDp#qz)+BmIzcGw(a1rl+I~-p32N&IX%Fq5V z&llgpXa{XPD$7|}+bL9mBNkC5ev+3`B6BdEhV0eqS*5L5M2gL@<-$b1S{$gD39v;b z6jrami%%4Zmu1*jP1v=3A07E46V=Q!X=$?zOsAqF;AlWF5>nj4YQIJdlGnF%A@&jM ztfMiH#}oiD_Awr$FE&7|7a_lzkc8)S@^d$Qo|zO;E(K0u-cBeIR#mScl;?7Bo5~4{{}=3I$6<82ey?Rf4&y02N3QoQbsMX(D?zhb=N^cG}!0;+H0TTnLB3-MF%Wi7wo_ zawDd%o6Gq^HP+At^Gx4?>f0gtQW!w|JD8a*DjCzI1nDEP8CO(tbX8{_2SCM$e$Huz zF5$*uYY6Wal3|nTy4-_x+Af%^X_uEym#%YCp{$JIvULZ+gyo5P(_goxs5QKYW-e3! z(+RLRpb59p1iH+zmI#6H#dRN{4x&>z5clG!>;+co0!gXkZ8k3u)WpIVem33Y0$Cx_kgGEd7Dr(aRtfVP@63TNp;|gyM0-Ud zKykrY(X#-Vyr7;+0_vyT8&Gcl(Df!rB4yf35x!Y?#I5SsC#lWOGh^85S_9k6d zC-U1OI;mYpg^d7!4ld%(E?s|wR+xBMd6D3}p<;q%0^)as)FpARp|0#9P#A^TXF>cK zlgP5RKKaGbyTlU{wy@d zU-n3mMy$*_Gp#riD#}qup==on&jPnh$C>}-VkBm3Suo7e6>E8ip+v9PuP4z*#o85t zh^WhIW`oE$I2%V$`J1OqP+L|?dPOzvPUA6&DX=JcC^R1QNy!>Oa{(y6$(gn_PeTTv z39ddoox8BJf6>)~(+E(OHtcYuXgNW~W50cwJR zaG2H`>z<~BEu^sDeL%1- zyxqCw$azmx50Q#lm$hJo_T4~>Elbk%1uku#`CC<*NqzY{H%+kCO3^1TdR@_5Xe9&q zs88J3Qo5NDPzme{1+s}%1d-`%Ij-SK%c~VRN5cvg)T_f~wWGqo-4DAeq_=G+xPjGi zPbK3j%3$jr|cHDcK_Jza0`$`d+~dMa~{vkG&{FV}G3VNmLq;Aho|6;4JVRzvw4VNtj$7V#K` zW2GWH=@oJ|HcDw`YX0~f+mn}pFDO$|hiN_#cTxet*Ql{#A!3QETn`gWHKEL$vf5Oh zHOp(m3Msrm%<*C30B>A|3c2Biz75!6sREtfK!Y)kS00P z1GX?oi`yb~-F`?MI4{{GL`!w(Q_xLtBM@88`F>i=1jo{wRoEGm3TNr_A z;XpC9K@7>-Vip7myNx)wKaV)zrMt#qbPqi+jVZJfRD@-VekM6g8G&bygXwbq5A(oq z4yU5V<%9RRAJB=fR}2n!&v@T`_do~4%#@jR9_ia>I#jC>fSM>DW|bDaH` zVx!czo@xNV#Y=p+7+G!C@L7jDAtCnsyNrIc@@0KxMA;lR8H|i z2C3`H7DmCyG+=(vi)ni986~A&>FO|jC&~yMDE=XcJ71$5EL#1z;=m?LD-@@1&o#I#z&masjkKR3a_mZnH`hRB-~|BGANB11;&yBs=; z?z4BRked_6$8M9l;~aaUf!Hb73ZtnoKUO&6;NDTds7LYL^@&V`0*6 zcGF1nA<1az$9OfDq$C;nyycr4qT63x{ z5ba;UM?ZGF_l?H~7uf53{0Ber*|jsJxAy)c~% zT1ADT*tJMVXY)dgfi=L?Pm-7{Lq%=0x#Myy7>|81M&W4hl z+O*{p_ktcxc_W9iJ8uP9oYbsi4z``%m%zdbOuJ;9rMx0mT_R;@G?r7`S#L>>!7fre zH9%9flY1~MbLE``G3@_r5Q|kBeCYZi3?SuJR`u61_6!`1>ETB~V_0VAeh)mxaD`avEGGcY=Bm^vcwfZMNoqvWDTjoV3M$+I8x1IeNV71FAcDtkcBq%c7#}Vv04qo z)pyea_pR@I%ix*J#M?Qc20?XK0M-e<>+ z#i>eLP*EaEE*TSWPr~QG9y@L(3xz%?~@?c^?_SlcO za(KqWl|V(YS`4|E%Bk{s9l$94sek6;iwSPVx{1!JvSY>uCeu10jW{nNSkuKXAypjr zN1>FW_;0!jNg{6f60=9qItj;5C0>ZCPO9V(X)w5=@AUXf+3DaJDOwz3$V zs=kJQqkvHM|FjgpSp3= zl4~o=+bR5znd-+&UF@?p9guOP?cQpHO%k%lS~AB|bf((2iU66E2=z_L&1%9W^m}gW zO_$ODH7m|6 z9fae}uaV{J!nbf=nTM1-lkzM8xOhkdnb$+ujD*VyU%BGM8XBfAi8FXrz$cS2Hg84| z@jiJqIlc94(m^T&<*XpZgY^=M&`>Q3M~`uCnk78ag1R!n*ARgKoJ0YoD9JzJUQtnf zkv)r5we)%dDfHsK>Mz>Ic_m>Kb3qgcs`tpC>7vX) zn{8o8rf@NTM5VZ1K?dlTz0U4``&FAzGP@};@Tq__w6X9BYhldckd2Gq`!p$}RDUTi zv};oA8=Urc(*Z2`KSR^`&a4XDL|m{W(mp%lEnCHKGaXHrs5L2)W?{KziO%Kcl2oN_ z`jna0cmd=86G9fv1rXb++?dRJ0%cX}1MZf6jkIIA*a%vpI}Fi9iX|;;lTPTeqHRnW zbm^`PsQrm7V4zhWolWMOLbH7nxcW9ux7l)g{mS-?9Z%_Mbe}nNG|RLtXq~{tyxNk| zaz?4y5}#^X`Gqu?XEdVOx-!2%(5Su%dlI$RNL9LAZFNZ9KfB_1*blT@ry+SP%18&HUdv{?sj~_`|Z5t)$rCgoIL98@!}mTj|86O zKcRno!UeULBmhNV_K9&H70N#=hAe7H=t~lp{>`Aow1py4T97K$U_Iwk4k2>M$D-`P zP)hua@vIBxDW>^R0bgKq;*-hZon#3_(x+5LGOWIomX8Y=RsNenV=RM#inVvJ;L?_ig%G9ktFzXwVe{9z3va#KEMHpcxBw0>V6M zt5OaTe=D=ua+0Tp8&q`yk}hyXTe!-9ssdaNWo;^bDE@EQ4C_@$xIpa2ybIet5@_XFU%{*8V-X$ zW|G^m!vKljq^Gzp>*D}23W}ko5m6cVw=FhXTt!*QQ2v0+5CfTo#KV+ z4*k=S?yARDaNx#ymuvlbT#tzT&UvA;e;) zZ489BozstATdFz1#PJhBhFGP>dpNj^@B0Be?|7qo;J)MQ*Zk$%SNj(b9O%z27G5E+ zjIXNV#axevJ@GU#;ZpMvRC@}n^A$0GJdIu@>Ky>cT$Z@aT%G^I&r2b3mRkNcf9AA4sr7prBNZ`s}m-p2~V7R!-_{;aP;B z@`ui3%uK9ya7)6%5EBY$#5?o>%yK&yZrGhB*#Y`X{~k^*mWV-vQN4RMGSWVcn2F${ z$!)82*tzqxjaaf~8Pa@h=HasJnq;~G((<0Vi2_9DM(^_Ut%YdYAu=pdcozndW<0u; z9h1YE*_kaIpStM`y~+2EvI|<6m5i|yrLQwm9p3VTMgE~$jab%+uS#pdPr(5Z_n~#X zTjQo2N0}`LBQcz66eMS7!Df>I2Y?xffRC0c64W;t5c5yDBEirr2$^29E@;;9bSW|2 zY_GOK(F3>DZLT~mMXJLc*dGh%_Ob zu5EkMV|GY&m6gFd5E{3nWM^GU(Dh%%uIes=?p;PHQ7|o|+%z*8Ba)w>Dv6w2)|fFB z;F6yl2+ssedN>N5*I~4Zm5Jc&>wfcgtJ@3ATmXQm4aEqFLneFQ96h-RTx&-$O?-ed zUup_@(Q_b_tsf><(aw2%+_P2}?-<_krzday)8m~3Jh;W3yJgtf#c@Q3;cj^GTOIFU zEm-=)KZ{e7m(DS!MHV81LnUKOldt2RIHEF7snEay@ym=85OU_FMxwTTBqxCdgMQei zF0E7`3k_5tWRP{eV=mI;KNAx(P`(;gfbG%@RHGt6bqOb*jL0zSe%+xK1gs}$K+cpK z^R3E7HG5+%O0gx^6H;FmTIoQL`1hVN7p-X$K&hivCb9@Ywhd)HOnE~_Se{ju3e5kn z#iz0kjn;~E`PIT#y@^jItd?v|zy3)==;HGDH65+t_>hjS(b3h7C%pC1t2>`G?p#3f zz&?FN>5fraUz&M7zP7sA!Y4H-#xV2Rcr`=Bgu-RpA*Qg;g@j(b-AN|%Np9*({J>lT zGt(VAUA0C+i%{g+pjSml*m-FhN3F@mAllinXC7qPAii8#fa)q?yB~5^>>P8){ zh%gL=2iE}2)@HJl!y2oO>KDCCP&`hNTq8l~g!E9ymLXXy46Fz9AdTw#{pC_h_k`$F z0iLz!orHpqYN?p$))6XD%PPj2FwHYXk&!uLHWS;H=|HzlNz0DuZvFCNWy6hxMNN$p z+@PvOHiu6s1kmq-gdPFC5D(tOW6ew&4L4j}EndBnftvUdBaeBgIj=fsX}hqZ84DVv zucNfG`Wi3@e*-fzR2jM8;kcCWu{sCp6W%pUr0zU0JZF8eRy%tR+#I5Hqw88^=s7op z)NvEImBsf>{Au1$6YCg*f@Fx7i|3NIeYr=lS8`hfOc}-8su^fn&5ReZ+>+{JSwe%w z<{XIdp9XmK;t&tCD1i?@H7{*y=0u3Y5!+p~xiM2{_(;2a_r z(@$h;cB(m0MwLTxPu2O@J9UoXyC_^q(i78*$H1}<1bHY$$sz#APr21%!>FlBE?iB5 zlji$36BfsfjB-vQ)HMTkzHo&1td*-SDX?gLT*~r{P_1~`;tJXC(3=|t&b*4c zN_|oYRA^+=EhUS*j!YgR-`L*H6$~o|l1nD$tIb5>WgQI#nN9P>yhOav*tQGx3y9JX zlsuL#QH3}uih9f{a;WAUWN;;ZVwbS26_e?*D*#blf0bsd%!z=BTGS$ro5rxE5<+@uWXc-wfujF3%tmb}Ex94j8VOU?3NR zPPH{rjE%Ke-o&zQd0R$(D<#r3gD8VFOFWVc%NZbmdA@^Db<-}6P7t&ZJ_EfsZ ze_R29Q02Jt(?8k2=lVltT`EXHsRZ${$SMrEGLWn&r#C%!kk|+kQa?o^@o!=Z5V5jO z2}g*6{Yw0=?qcN!l(GCsaFRJ^r1A9+d?Em@4L1E$Rgd^e3yp0zE*zw;u%%94)*`CA za(ynhrQE}ri7T>uTU2)`+Jq4v9}LJ>qlAwYgUpbu zT8+pXwo^@k$KkT!#dH8WOu|KcZn;}=hL|ViU>x1fJ|5iS4ld%sB^*`&4u}YRCNT;y zl~>Txj7mW;docX~Y&CT@8EJ8aft^jsY}-p!oyj!Wv(N-F$~#-M@nCGriC6?SeNE>! zJ|ZG!wu|Xhzcg%tf&nSK3`;qCgH~jioyQZzXz?>kjtJ~^Wk-dDd{XmquNmF7Q)>aU z=@=N1D9B)nqk)@P7PZ-QiJ&K({NAb%wyJ{3fX@=pr*m7C*E8rRgZ(~+Kz8RFe`aVY zhFg--c93@14h`W1CG%j4ZmkKC<^~x6hJ|Uh;!8AtR993vRO$@%O0q7dR#7#mQ#~2Q z?>WKhUQI4D1(35=49(xOBfC(SJ>77&Bp)ET>$x|H^0APZ)09yg1I_xbuKt+ypf(xC zt%7h~$-pTmBBhXv^wuGi1naXIviZ+2i>!H37x^ z@j0iHq=wihPSH4Cn&ZVnQ4#4+moWmN);)7HvV)QlZOxC$ky%zJfG_N)Z9Yhsn=Um9 zb*=%j?RWEQSx`I`0T6*3a6EwX=ivOEju-t(7H2s6W~9!?)|ZH?9Y}Pd0x=oP`KEwF z)xRZw6NzaBxao@vboN*U)=$4w6**!U%h`gV&k>~!+O5&#ik!D}9g(rtBT}zK( zboieo1w{M<_5Gl_Q%6zipoq;xRaVo#wczQ+gt~rHmMwR~BIIFCBvzp=F0s0g*msnR zM6-z%Wj~b}&w+2bXx>5-!kgER8E!H zy(pR$rw(j%@ujm6=J|zW_phEpvs=V8Z}FOo$`Rk}lxj9h&4Rt8P-Qk2eX=5hswHvY zGom6AnNH_fLMi811!r;mSUa5=uUZr*mvxeVt~#l4OR5s__M44fsT%jfiLN@J;dmRB zcq7c$#XQiR*cpEFI`=8joElBm$_p#mBCz+7ym*=lHL3In}Sc zdYyu3pDKMx-2G6o7h7vikR`6>jg%HWt%cAFt-A4Pa_WMB&)N+`!WY&M7s3p~z@nz7 zJBcUNdDI6|XspHJ>%=V<=8Gavk(9{!2rI@O-cPSPV2vap&$*WZ8I!K0Vi|IA5iCI} z@6IBB9jFYF%tUc0NKwirulpYjSrl)uKc8FwaiPl27!20g$Mu%5vznB z9puNZ0(eY+*xaN8DaU->szjrVEA|R%j^{kS_08l2H3bTp9(B1`+2-Ck$&Um1D(9BZ zv}St3{Nk%6vR+WAupP156eMN1kfv@>DWsJOUn(Lg&9rtDNO?u5Ou_K>x`1LlzgC2n z;mtfOgp~K3(#|kh%`@V%x|N)WPrsv>4?l@!oKUE#UOMFZj5SPR*z-$6$mUT2S6pej zQxO?vD&n6cPKn0IA`t_G&P779a1(-~n)h6oaw&-YE#xx=iOginpZ0TRV7Wr68fl`_ zkQb>2(U|12O>`EikjgAQKUEuO%d%;!%b(wogTfE(*|iHcBMsx$cfhJHEQ}|O7cNAs z#g3~EWtolL#{2OJFG)~(YnhQ;D|B7IFkQ?Hi&O<97aCZ!Vv10vfm79-T{m5-JdGke z+SlaVJ8(KQklDd30L_NwnHVvJj#%+Xk0k|y^Hqg-VOt_j`gpc8KiSWogv6TX^}yML zsNXYO$>SJ=exbGZPE)*uh)mi*FNTRjYkH2e7bmbwvQ&Plt-LkZ`dSIilpMEea{_yD zDnukCxVHd3NiBBM>QtO>{?S&H>XmZ1MP?z$GRGTW1hq|dJUir$@wh*8IE0|FBl=lzP z%;b8c$GUEsdD<1H0kTXimBvFB)bJdLkR0K%Xx4KA2fzBKwLhOU}icm#2 zN6#P;s4HdF0DvN*1SwIRl!&uS%$f*8HH#`rnVzB=)i6Gk11}yUiTPT?36mLSCjv9G zq&0BXAI?fWEyv4uk+QPUwCMR~woVCn(Tfy0R_j7YmNOytN7^H(naTsVC=n#Omw$;% zd)3s`s}jzpdi%Gy>^c=-z1siiFxDN?B1GqyJ;oNGXs35=O*sr_*r9CGnytkX{` zq5IlW#IN@LT->$|jjRB^GC_%=7HnlI7Yw6CPr{0t&Y6XwkXNXpvfbadA>|A`tRC0v zV!^q|!EK1k7b;XIsl54AEol9Fw}_x>DRc4il2+%R`szY3op`j5b}4W(`!@6R7RKfH zOn^r4)&*IceY0z*3(Z>5`Nl@aJqI#gN^UNt*iF)_iq%%Ai+_xHS3l1(pRH)!5H?II zBbeWQswO4AeQveW`46a6hWZ_#k!ZI>N8h+;5or+v+}!7mQ1QSF{lkz}q^l=okY?Ua z>EBoBJOW@oQ6dm^?s&BDcJ862B1VakT3X5LbjwjgcQx~+Orfq`Thdi}ltUB2a(0;k zSxFDD+@;B5bIe?}1By|8>}RAU9J>7(U`85OArb^A4KK^1cY96Lh@DrYz{lZ4*~1hH z2-smVd_0USpZh9FP^8&3PzhcwS7q`_h072~y;`?EX3KebNP*QA714N4G(Nn_(=|-Y z6oDX*eW~zQ8GsHI3DR5#hO`JBG9Sh|E3?hrYFY2H##NlK*qs;DT!xrf_(38C0a4)S<)uVSdZ&56P1qZE@?UqYuC_aSN8$f`Q7U^8w{nc=`#CBIN~?9rSv)-SiWSBG4WoX0-I!%vkhBUKx5PSFevX!sD2gI>dCQMarq8Cx+C__Ui zZXl`wwoI&*yJ{z*M;9~F=O-sEU}I-YgHkP-SPBM8%tD{pVu=MWiNNT>j9B*;V-!^- z67k<9`ExH~Y~vNURE1D9&6aWy1}YZyf(kibPDgF%GY1#edPEf6L(2GO#zLb1Y{pu5 zhBPOt=hv~1_KY;a2I~6eM5P<-CV-Yv5(DRIYNoBr=k2kLV6~{O+7O`3;%s?6v3)dV zRRCBmnW)ca63B+1*HK zUVtoy-lSUiqK5UuMD5&3ijAJylE*0KZnIZ$#u@k^g5zRz%;IT_s6GLmyBz(hM-Sy0 zTB?`Fp8|!L`;yxQyw7`rVCZB<-9Ag2oLvrrdvX4{2@YYAAPVS)IR!Fh$)+jX5V zn<`RSM8pU~`79Wl1)rw49F?rKTG`vQ*VCeVCVoCeqsB4E)56M~AYbRIC}{%E;*&2a zeIa(x3c1p=bIFpi^uP?ffXQda;_x^*OJ>D{h4<`g9`-Kt4izrsk37{(s;ywLLQ-k$ z_*GtR-IU@ZYe&jI>1qHbp^_F*);mwsvYcGf8WP*HZfG6r&?uKa=A)fXhki3+BuE8= ze(TLo#xyP5J(yNHZQV0lCi)C(*UCvMOfpXu_?&)t3QhI-stXT#bD-W&9Q4icR?>9y z;!G~&87Z`RV%*DN6wWvT3qQAP>?FzoSz1|S&lV-< zMx1GN3qU!!v%#b;!7Qv|FG*G8d~Zf*Q*?(blFUtZ4<{0D5 zGzPI{a%5TtY6_(prOvkK+!)gv|AEz4*k}62+S)bX$-^}WK z3r^>Wo6|Zx`}q~3q+_x8M2e^Nd)Am1L|d?Wc)2kpg9kpw&=*z?ZulBZfb#o%sW4YfN--@% z%YHrT~6Pi&;1nL~`+tyY+so*Ar zg=|(dm}Z0FDm*a7Z($3g#I4`iV-3cnvp%kdE__abceXezT}@4r_-V9unZDRkZj{8x z9O!EFJjmeZI$4h(iFaW@AE~ z+;t1-^7C8S3M`Rl$(*^>4}G|*^L&a69@Gn(4Fl(wK~NofFi_b|r9A<8UxvdBOXQcZ z7BtR%HbD$Q77&3al8=IeJt=mz7AA}@^lG(i16XQPQOsd3>@y*3)oA9a69JOD&gyB7 zZq0sapVU*jw-*2E!VT|4Ec~?D-zvwPwxrUc(0=Bn53@CD+a?vcv3#XF$v#U6Hfq&# zp+vC4A-(BqhrG(B6xY5pT#{i$LQP3vj}uagx#N*=;RSM&NycU!Ha!Y*#wI@3#jRe*ailvZ>uVsLTQm#HrW~3+Hc@n85*d%ow2d(f z&FlVEIIi_ja}pHZ^2Sund81afAbdxwE`_yZM!}(KRC)V`b+ddvORMu{uXTpfAgxV$ z?GEy(X4Ia^Dtd?d;zH~)6xSarGTaC- z0>Ts-4EXw7KN42{SdQeinoN&PJ0iUucYc3U?UN}du9Hz)ud zj>%z*(52Niteozqjcy@K-GS3avQXP%22XcFpA@?D)sR_3C%o?jgRKC2N#LC-C)Jrc z0FY2?vEM3eX;Pu=YE7XE!Sgk!S(j*DszD3VDD3<++qU_RrZ$8qCWM7GozfwhU730- ze2@g1sJqivgQQuWx9(4CQ1ckuZa{1GyzO8m=T1!CxD-RP#+@o|?y8Ih(@<>%nS+}oCxJ;Su<;B8wks3B=fV@9uLR9;2=0MO8)R=uFMlCrdy zY`y6xBs5lluLwJx%eN{o05MzW04!#qPpyfoQz#Xk^Sy#T*~;S@Fg3uAL@mdd4V~G+ zC}&u0ei6CNHE+Q*rXz24F*7YF5VJxk+Xl!~M}>oxWwOb)Dw{w^NZIs>^tS-i=cy(E ztGuRMD^UUQQvj*IIA78l#G;)MORn``f{Z2R2*?{Hdg3!PvH=teq8L?sT+=6=&Mz(x zHCplyEDSYAwQu)TU@c*ur>j}6s#8HxkXoWR{gSjeU-U-%@w8f$HX@0lA{9_n|H@y9 zHQH%7BASHWJ`AhHRvSaS{@Lhi6E*! z0?|s(I$?=vt}EB22J`jc-2{Y6H2GCthsb^UHIh%o41c2Vd5p3Nnz&r-iR;vUC zH7#a+gv31?HaEQ>Y!xJ_b^OUE$WL-+1Wa`;9_Vqw z%k&E)QGSXQ(eTr#4oQg0i_6H}892R+$DCzASop zW02G58H>$9rr8ZXCDie{@rnkwCD%?@wx}R1=tD|wP-|u%OO@f|#pAveFzqj)9q8f~ zil^I+sj;+!w0O1IvX5y|*1x)hPPiE>E}+fUo6L*-A*dd&$Q?+1BY>ON&L~Z~&fJ2v zYm?n*&FsbK7M(L9H&rT)m|Y{{GB3~s1gs+^i&Dnr&gOshn%49rlV~LrLisuqNs&AS zQZfD&|CuM-V2NLmwo?Rx+D}MUZlx)mxcTO4oM^h_+NrikW!;jnn8~U_p)dmdBx+%+ z2-Ui4<)vV(LNAo$;G{$;#++?F)@Xu|4HIGhFWCjXlXyeqdEPRg46}IDjEXSnWu$`r zmnp#9Nl+rZbE8JGxM5zF;MS4BUcMMiDmzl7S_=WQKiCKjAua1HWr??#-koh4H;nHI zkj41eCAF3qVE)5~Xb8=cZda-0+84;#OKkB_Oal>D^9C8G2}K&9(y*uw;*$B3F<+0$ zq{J*mczjjlPy`u^i_Ixh5~`V&D+{JS>{BGDgTm*203*Y(+JN^$N(HD;#g0i_>bK?S z6!xmp0g?$et`?kV?Xt-OuqrqbodvKzZ9}eF*EAnnIS4Nnn3v8T4{X&snbcW=lheRRt|4Ld7>?>5`4m$Yt58xQLcn$Di-8g zwk>G=RApm~scN!xow~AsV=hf+z4$$ZB_(YWAhsW8TI^iy8J#_wZP(Oo*nNz3qDaE9 zK~OWHBg*tt^1~&Mi?@ny%LXC|9M;t-bEUAYty0}>`^zn$;j-&KrkU)dH?0ocOWcxS z=QB6i++vt(e*J@gMX7{CjOq1#T&?ez%y(4ON@832L~FaHZY@LTY3C$BQ-3?5QU+qe z%ws$DA;98eI{=mYFf#$e#VncW?4^Dqwj%&PE+r_juslGfa5+1PBx_h6b*T+CFpqq? zI4{s9l+aK~HbZrsh@7#B>XP(jMoNxN_sl9o(q*U}jygy=OAUY2&f3L^R3T62>p&@g zh>6p>Z7`;YK}1{8g29IJe}kkls(cGRok-3Elv&Ilkt**L@|dRfYew|dU^O*fW*0_4 zvWv`nr;Q-89XM%v>ZxR!fydg}ZL+VeO3d0kwm6_W>r2r*FoEy8R>o@ zNm-B1GNdCy`edifVd*Y6R%O@cHl;-M<9G~j?g1PN_nS?wwHGf7m^K$3ZZvI6-E)aY zJ4UT*NPnm@&keAxP7&D&Q8U$w`4EnOV zjFv`*=w>yG4y|2nm76CeJ5y${7f%@RDT zW786CBc-Qp$#NCFNy}Ek#;qDtY)nCDf7-Kb57}w0*~KiR;HyBx1hwug>_RYU23~DM zjr0KOtfl^_5(wjIP6+^9LRVg*k|9KS#=bJ*j52_{c{As=!E+;0l2n26%9&o4u-TX1 zqiuOz6|IL>MDn~4S}QA8x|ogr(HP?!}vkb05-&_ zZpQWiRF7t^B1fLahW&NAW?|~h^%Q}J-n?*0!_%`}nl%>78I$f8l1|Z**ISwhX?!xw zfGRPFRQAJlVT+g!bDOE}G#H?u0>C=QE4-JO5Z13Ob*#O09=P(gAXuYY`P;&k<$;iC zz#7B0l$?dCWV+Etj>EfEh9ygZsEENl+0sB2UMR=UubS?tg-eGbvqNr5>l8FQr1qmb ztzta_7`~;|jjrhsWNFAc@G~p*qJyH_{(y;)t=W}3-O?@%O^q25sGDOp9cOd-V+6|X z9o8Ls#l%J&hdL+%%E4e)$W$dYx0JmKjn392`!cF8#jzSuhm1UAwyfBng*eS9iQLZn z4|4hE;eDN@007rsO|xWzd2a+Px$IZ0XPfrT+jh=*ow#HqY2DRaPQNgP`DC}Yh$2K> zhOYF=lUhi#Z2>YLP!>FpVd5B}8|sPTeNkXl_AUqIQW0qiksI$7It0NCm1}f?g^0mA zXRz5F19-|rgg<s{-I^Yn9$u#q{|q%_K%M zrwb;^tfExZMn0f%YZdF+6r0=9u~wl$8>MaImD$+NM4=VjG)MecAexCwR5YVCORKq_ zNQWCOmM-(|DJ#eeC(8Coo3eVMQfjC@gb)ec${sQKZYqi0zf(_()skIR{kMJ%D5)C- z5$2j-qp`>=7)_GIB|%#@)N%=G>y&HFP`?dix)OfU2!k4eD6`qQ*dQJxB8!8Ade5M` zTMLd&Mh(H3ctx!&do&kC7HHyM3f8OGY|xT`mhc&@UTl??q(Gb&Ih+9seKe5jL$x-F z9$s<7CDklsA5+e|NKe&ra%G7q4>3bv%9Su*MPQHd3i=wwyDB}Q_`*^GY}+46TQR>G zHfDbUmG~m-1GQcnQl>qT5OYH$>Z$}07hy!{#Szt)`pm}pUCT_S#|TP=r)ydOHce`L z+*)Wq)&k538pH+f$}&-g&{M5&)sZPb6HM<(a{-^l^EU%_Vi4R+eOTcRAQ6uOsk3$)W`^^RL>f~DZYf2AR7-gM+%Xk`kDl>JL$T*qW@4mTFvy_|TV4u#R<3qxCEa){T_8ZF8kkjy@h-6vNMBoVrk-hQ$W~5#Nt{1hO6U+1 zkc2CmKUi*ES8-z+b+$-=nh!Foj?xbd2qOcl9H=JlG zAFHUyie3Pq-k;D+<-0{w;srP*dv>Ur1f{BN_e!`4b*)lCA+ID~^WxihpXg@~|Oa{zS zEf);Vxvhgqt*!ZtUINo(wSwe+6w&eHd?FlDO(dj=laQS=6-VYeERJxBvFs#<`eZ+M z5Ew0ra)H~7Nj-;j-d-;nVIQEYc2XS?M$aa&1ca#lP#4oL5`XQRI^@2 zyVO=s%prQP+lb-@GKK$>wX&>V3GRqr2LX+@F+(5uUW|7($TV5~}ECA^k7{09<>J9=z9Iedly| zjR0`}JYKkMwX-*(OQwl1R#*bO1P-$)%n_T5X%e$@(Wr#V%8<+@<)pTx2G&di)Y^!$ zsyQa>$oY~CEhhY-K2_Wm5QQQP5y7tm#q#U}MjzeeE8X&aU z#W{@c8QpA_Hger32MvvO6QrHhbhheXgx6bPG|yPR)oiAMFzd0XFp_+foe*x@T5`T* zpe74IS(pa!O3t%2P!U@BSzkT7U}JpVa7iPQx3mPDMd(1S*K8;rhW2So9jcVm>SHFt zXKki+T0X?QUWF+0FgqE_DSZp7Z>PLxqV(;SgQc7Xw{KQl(J6@N@TQYs3WYs;6Vcx3 zK-c#6y_9&u0&2G+##-re0Ra#YT~2t4asL5PQ9L5q6BD73|ltiYjImL>{2P?)Kh%+;ssdr}lX9?Sp$ z@QMPXwjN%THcL)s7pJf-bMeU$wc!IZ2a1l|JfK52!1agxJKnH)=xyEsUbqc!f9!a0 zEAAfPxWZi1iPnbMjFPAp8m(Zzs=nlJxy6bPQNC2~`oZI?5E*y?9d8jZg1}@_J^##- zFxq7@klbbi>+5v=A$s?H{#|dM-tm^r9e2~+PaH4Y?k3Flv=f}1e_MM+b9`HcH9hZM z$KoA*?6kI>G|qHPWuIV^POvPb?%%n+Cc3r4vKH|jr22=5n2{-e>B|r5<%$3DI|Bw{ z1}y0K+|866r{_1loA(^%T8q`9&jK*L@SwNig|`x2%l}FgDF%uv)+HlWf1p}HuyVLk zQmHdY19go9L?Xo|HCrduWz7;LG@3yVlip%k8F`ZUD0PZ&3o0*||Eg%yOq2RnuMFoe z)z-y+?iNiumUtNHO{2@2G?-kFXjyG7-15kc8&3_VHB6#lY}W!->4>t=TbYPXiN7Pa znF{gt9`8f-&YKNY&cTk1BwB8VyBMR+rB+Q%hqEnH$a&0ysrVCxW3P3h+4|l#J2i7` z%hPq_kaN2Z7<$98Je+69TJE(Kd0BIHJkTOuQ`k-nIh|s$=D})8%*60406XVry>KfY zYe{u8c~S1H?=j7}n_*|iVpNJ@Su=T@2@C%&(5t>tjX9U_YlsSIW(CPx^cXe1Bpi&) znxn+kSFS@A(sX{TDc05mIul0((BDU}v)u~2*(@ofq_$gqB+I}fg7q3jlOiaQKo3#N zmT7Gi(c}`HNr?JQ2at;*gRRKrSYvJ!%SNl>czYM^KBK2REQ!!#m$Nz4g_{Ky>F5c5i!}+r5Zx zCzh$mhb=ZS?Nwa9uT$Bh9ETUPm68e-Bt;`hm5Ds+xm1pIlWH7Bq zXr%Sn4p-&$Yb<9~#a3e_5a5xe?q($eMo00R&34H|f<*A1TTP#{SFzJhJ3AP*N}aMw!1O!JgW z_!3%87}j&?du|>4H(STb>KR@&~wGh|=W>u<(BbjP$@JD9{0@E&c(!;ifqQKc%lAOMqAM>-^o*T*>g!w*eP?gLW)j4Ab@wAeJ`q0A&!W5 zI|ZGEQ|llV5IWIG;RB{Uk~2Nr(`bRL7a8kIEyDP~>`1`2Dgd4Bne%ZPj%hj^>Fj;2 z&QG(M4no|qFqfF)$wN3>u)ev{qZSb@R%=IQ&&WDS?MuKW1~pzTSR7+3zK722z)pm! z{)}Eq`C4)R!(x8zgqz5< zwKPqZX=+SsW*F7p)OtatDR}+X3G*tF3`#{}L85CthEW z``>4u4VbjBwd9M}xMcy!0#o#7s}x>gJYAce(R?VP3?+$C3)yBm<~dtt6Q{Y>vI`dd z^Hxdu*Dn9rlJi6bW|wV2{9$ws6*ySLcWHgpApvq}PHwI4H;X8NAZWjxGb`z6EOh0p zAX=k79Q8Hh_<>tD;Ejj;oB!zep?6M?d;030r|pdU2tb~a;vy8b)F>*;muAXLix$UN zOeO|1!)UKb93-%kU`_E7fgwZ{X=Uz6;OlfI`vt^HUTtp#%ipAMfhkr9Z^7?GunMopJM~n7PKji z9hEs_w9{{*_|+-o>7TQZ#hE5A>S3p`{8p>fyzMJ*PVmttLLT=`di=w(BzZ#~;!R*V z>P&5%O#?G2YF$;{F+)?l`y603B7Rc>D7{--*@$#prEokMFUeJvhw;1JW{{KN=oFq?(h|V27?BDXLqemX{Pk8p;-A@^C03wRtiORHf?DdEZUA^}Mnt2vEUg~%DRZ;k?s^ZlQTia1O9Wcb`iSn&hz({ZUflB90s zv`vtVdJANYl#ddoeA$Xra)7dWl|N7svNq-`YyTgA8R0`Mdks^gI)~=EJfrON^>WX! zTMwX0p{>Ckk!nV&P|6>IJOc_MCwq~&XgF9En3iaZL27$Cpal<>WjH**)xmC`W=beI zcUl48sT(40TaALmHNzD&2Uwp9W;C=eRmoDP@gasg6za|XGn8l^8|xka#C1UsWnN5X z)S5%~NmXYZmIfn3gyx6$0HN$&0{|4a3q=A1T;HHSf8FsT4@{4H>dxIy8DRiFrEh;z z01HHgM5d3mtmR)}j`6=JKH-loo0Jhz*uEz#8~_1U{{?Uk(FM4PG6cY^K#ZGtThu}& zrdbfjUj?R!2}YLVMEkS`Lb&^>!~4A7&V%>UTVAvA*8v=m&v>y+O3abZ-^gsymIA@8 zZn#!i3=W!UaZV@r)jraW5xCinS~-6!b6XVpSU61JXKG0|%mBaY$aK43;{evJs@JrW z0ZGy}%?yupEyi?J-9;-u7Ooeos8BlCg6u_oIMG<%9@2$5BcEHaY8~1ZhVLS^Y8~4Q z+Db1P5llhX${s5iw((hjocKY@+MGhuRa>qOP4n0%V>YYNS5FZrZZ-CdO+=v!0_m*g z2EhtA0JBm|3vB|#6=+yEX0=SDB-Gs`fXKRiWnrtYR+6)Dfo8iIKI>Efa}vIUstzg( z!VFCAO3bTUQl(v$7KRe_&gNF8Nk{gy(Y9?#tswx{#hp_l-HN_kepoILI+A4Yn&CkU z)^cKRO6w((xsga2%5m+E>$%6F4L`YHq9ZGB=~vQ>iW(7>D6-i|AlKPr$i1t%l}Qj( zTr->(i$FTMZ6L1;^Vlj9bE+((mV+OuLOoa&F$KPbD@;&)Z(bIO`>1Ej#w~3TDcq7F zrgCZubEMUW#yeB40P?tSt|5=7YyHr}ja5sORprc@A+cLNn-<)}HsiKn+u!36OuL!)ox~-uxY07^qbLsRk*Ow^1@*xw=WxlV1 zqmc61X_v|enqgD!5g=x!Lp>2x*u$hM8OR}=<*X}SGfRj#{|*$Q{pkKys@>N(Z9qDz zQD&Fe&PYlww6U-qaq0wD3k&6Dbh=ee+tR*uiZZ=~mr@ zfLU-bZ~EV=XqYZ$2}y0wm0gSxqGw0iQklMO*K#XtxIt$-1YVA--$7e-DW)xPs^v0e z0tIVMky_1KGQ*^3_J)2Hb9gODfSc7HriX1}djR-lXlf1W^~}Zs^-`x3EK$R9DGyet z!nO}(Z=J-%9@!OKBaF=WkOgYh6}@f^PN9w>+SGK2ma`XioMm?D6wRcnGThoP)~wJB zB&tebSkJ3^Z?i9dc2Z3MG0C{Et3$&Q>ne zQ(zG`!`9dlyp6H1;3BZiP^25)vO*K|a+%l2guFM(Knt^OyVYKj>MJ7EZ)S!d3|iLU zb|94pwnIZP5$zpBJ}ic}=UlZKJ89Qx5>_Kk`m)6n76#1Ld1EftlH3$GrdPR>Eki4m zO$pPIB1;A;YdRd1f=rf-ZLt7#UE?fD+LE-JqWg8OVO-w^R6~{)x7Ku8VOyuaj`wsn zy96CBTgOvRJmX>zM4T)~oB|6(xB+9rQSci%mZ={YA8sza_y|VcJeRg}BC4S<)G23c z;=ZYEd8*PMo11Wj<|y-gN^p4PxrG;m>)C1+9aCe^D1}Bw8o5}fTIfVSvr~&&bBGKu z-UE#bypdS2eoaai*A#AIE4flWEcqS9pIY~1U)8#=SbTf_EEFWu4A@+q?`&Md+M7v@gZ6ffeaEoi#S-FLMODrl4a}#PoEgAOp)T;^{*9s1jCX z7%tZ8d5?fS#V7T7NEHD-Gad~vX;+$1&Ayh%@(xjEa;79Bp;*p{v-fF!1kT~+7~cMt zlWFpIJ!xsQ?6q=H>}1H+!$Oz@mt8CMOBQu*%#^~W74{FHe&fF_ng+Sdmiq7>i8$W zP4~TNI)B@U0}}bP9}nx!Rf8>YCQm6&2w7-}wsv9^qbe5qCUVOS~+ zNFh&U)>;Ni8&%UrEjKB8%5B4lLU=7u#Qs)tlECKifLK;PaR9l_)u;=)n(f(+5^ik0L) zVUGU@B_|EGpjz%gC)0d__PU#}wcbvkuTbSC=2XqRAuWHBP%4N8?jftkl*(RG5Pd zZ5%<~d!q25jvvQiclZ44+|SaEkB&~(C*wGxa|GaA*y9IMy^f%+6U~~2c%kl=XME|= zc>80Ahwq!NKIm_`+xa!3tCy_dzdGo!UAeA?FIHp@yU4a>zOa61g7~ns-E5&UU8=Gl zD3u*EW7H8t2GehKVphRFjcFpIY_H9`?yx12NN3yd+ETFCt(>)KACa3BhNhq5$;H;F z1sMxQ1=J1B1u2HiM#HYeAVm?xL}Wih@p|B}=(!cO0aNCVmCS(R=#9zEKu z>qBnkiupa2oarIXg|l<*RED?NI;EM6Zejju>M|c%CsQ1SZ2<)8Q&0R<)@&A+5w^8n zD1;~pCInj^Ldif)xrzenGgTbHKQN<|#%2Yt-`y1BpM@S~tt4Bcg{HU;8(@hk?m!Sf zwfr~aQ{=O8K^F?@8TniduR|a>t=6)N)x^~Wch6C%G^Z@ee+w3+;7Lom3}4TE#&2ItT@ zL}p~YCjtP!nKsiz(>S`-YIJS@M8Dol1p9mYAN0W=^f4d(A^^B?csSxX3=UkFP<-(W z;%C(}quBD+3@5heiH2yJ5o58=Z(vyA?e`21ylwODcdReoxf=$u7s%LG)^!BXs4U18 z&amcO+N^356MtP9;aLH(SP+>-OB^Y~BA{G4RP}e z$!x7Ms&{Z$Ku-KjJ$DuhRY*mMj*NBHr5a{sp4*7}A}T$yS=r%y;GEA0vV#sl1$CV@ z;z*$_TA3sz7>51V@Mq6-B#lVQC%a^H*SLfR%^1~F#9*~)Xrbunl=c*PMa-s2#IB!> z^eoHrI2ox+b|yA+7C8ciDI0Xsx|%AZMb6OLP+egVikb&6rl{v1G3#2V2S6zc1+M2( zBb8nvEg1V?C6*>5*r#a_j90BRq~?rgqDIk^9h>hup)!$X2lHfWARnUpm_w+6h^Rba zXIwP_E{uNqp=5JR8~)j&E`5G^I#l4~vFZTKHN#X6<;EzR=Iqjw!|kE$N|MF!mg!Q@ zsjvymayh5Hu_^%Z8Nb?fYma21t45@YApjLS`B7mGGmhXKU4I0wJhZ;$BHVImBo9EC z#GMIt72@=oC|M8@k&hIi+Zo;D>199uli&M2-}8IF|GN)9@Syj;o~v7}c8CBSA!2%N z$bmzI0T6*40D=P`PvjB(v|gXAkAR8jIIixx>(1vt|3m)fmwv^IKI$Xa>$Ud;FcE=s z7+)L1#1rO4+>M#@tkro*ZKb}-gJ+xItw>tZ zb9qy`Jl}fxixJWnuVrGu-jlG=W%0t0bqRPdRa-g9(+txLGJr;8*{Q)NHfsGh?QhB; zSzc;|vILb$Ek)80ai*WBN9Iwcw5Vb$C%6?=*STR$ScS4(xaFmK(`Gg6HOjYy191zb z6Azj5nzechhSUen^ak7j1wojl{^@tV^PTs--}^oD zB`de9q^7)@HNWY&;+V48su1 z(v+V$%(E5BpXzQbSgW3iD{D+Z1Umx<`iru7C~+& zqNf0~V%aQ`nivw7G_|^fs25ytOiH!&`k^^Cx7HmFv$Mw7l9SfJRx`A((MPk3tUJb< zGaaO&Evt|8gjw`br9;`@T7}rFRsk`oYJ@hBz`_JoLo9t#akIeYjMP(Fo<^FST*9|n zV2XLkix1`QLIKiPIIgh79itZGjHzjQAnLg`lBRg*6<<xyPdfgR@nGuH(q zBSWAgZ5T~1)sxMX5RqOTp9gZ(r4P^_wLt zlwt+QHH7%l6ymduPoiAEI*ks`UmjopoXSvuSPP0E8O|J>Fp8_#AfF-tBAO--h^y83 zKYsq_zw@7d`@;`B_|;$iHDCVaUv}y8B}g~Ec6av<53m2-Z}_Iyz3$a}dj}3DhctOV zP3Xx1IDiq{5MIdwKpX~uX?^_2^FQSI-}XyN4?8(I8OJe4I)%yh980&7gqN7COcTT2(FouH20C|XAjIoeri-CC zTOOK1zNL@gm$8J4H&9=wvn8;qb%I(O!WrZt64M)bjk$S#7E%Pu)Gp8+GX@SM{aiAo zCB}UwF;-dNRt|&lzeLvyN42pDmW2GaBn+wmu?VS2MhIn5O7%=r<%`N|Z|gu>F_o_M zB>0`+o-g6YsBB0Ra7! z#tsD+G_$8JyYT+GQ|=?Y9VfmTOx69!cy1Y;HvCNMwviYszVv$4NFke)y8KZl%Vk!2 zq=u;Umg{0bN4*-7-h=W8P(wDQW1`+?6H8L`WQwF+F)|m|0h6)n-e;^LHp<2!7T$20Rm#~Q)DWUna>7$ zLj*vW-yvgx3nvF*Gff8v`;T0^^7H@W=ic?s`@ihVzWf`%@f$B+zBFw%(=?$Qh-kH1 z?d+_8$otLCY6o!bkkAt%c|rgO;J~3naAB7mB!{D)j{SOl%PqGARGZCanx@^|-50*_ zg}2^z+u(+u``MrU>7V$ikNEHpU#&)n?{yIX0s(kd(xJ2vdPylgPet6&lCW4rfB?Ja zaoimauTL}qqNDVIt?)XRfZbA65i{8;oQvX0k96-!sLRq&>>Q215oJn)kQk=Tzq!#3 z6P%iaG^S81!U%Q7jM*tc$j-cD?(DppO-3W_4{zT(MNTi3O|`1DO2OC2BS5nN!Dj%e z`oqq|voaN9Q8lMr4{v3vnhNW#2LG;Lg{!V2RRh_(@T7Vg&calUTBnNn?6^U`pq;S0 z)6A9Cen1)xkjQDVOe=m^g*5-C?kWdff^UD?4;rG3WFmiWE0F)+mhbwuK=sPL2Tm&j5)St z&}Vq(%nj9vkHvbPEnFrdtAD5fnpTvOE3{|S&hN686(+NjAf}jj&d*ENe8$H4++u~v z#(sYa||@|DIAAsii~f<97KOyi1|SdN)P zSAhB(y`}^$5UUKj`Z_Q~@Vvz>lXakF!fhA5BJo=U2{Yl8-N<-ey8Kjuy^haxH|Pc` z^As`Kws_aKQ`5n@JE(n2%_8IZwypi;qh^DQR!yf5{5{cPe4Kw<{;)oeEzOnThk3TW zb|FhoUtiCrF#QQ3oh}_!kWJUMfQ`+yrYgi~c}7k$0(c+-hj4gA$2a_?i^FPvAYxA# zA)kt1Y*AhWpmZ`6%3b`Zckwy{4!b+xocp6c`r}{wwO@PIGoJbRpZEEfFJC%3K8l5v zi3t2=ns#?rANJuN_Cr7XqYpiFAL5E=0)Y6LaBu(vfXnkm2js8aaoasF`S_0~qRnPQ zMAI}8(ed%|IF8SLzxVr_U-G5D`Rl*-ia&Vyy>EN_J&(C-TKfS94?gVn69~^inzXi= zX?0|x>KTy{;osE`uJ&NP20sA~;Mtl)glGYYu>O1a(Y zIBx2$KD)tth@T6KCY_b-_{Zh0^Axq4o7k4~AvEkhjgIP`=qwu+S4ODrULKmrA$mKy zi>lAFK{oMhn2qUxZE?45n$NAWAnzdj{Q{vPT|8xqgQ)IgbF?j$RBl6UkTL6(wr*9} z6AW4-L8y_oMNUb}HqP?NPYQ%u zZYB0%Ksd~W?re)@E_x@;23QnVm?l@0dn%0373;?NicP|h@kIT-lHx6Cb)IAA(MFMr zaR$>R!g)WG!w@hlBO%=6=k2g!uAwn@3Az(B_vy+0WWIkh?k3iu)kl{2 z*$^V?0B;sw7WRa;nzdnat;=-AJ_4G8+*>J@cyDn@1V*~j+>sLzRWxo8&3`Llt7bwe z5r@Df!m$h>i$sW8Y#m_FXHXec4#8TGFy9JZNQt5ikiF+@c~wr1W0Fj#11m0wjw&rf z4sk@?8Db?Wtt1fuQK)FFPyBQ;t#%xa;HT=FelZP7U<%sYBV9>$R!jl_!8s&E=NtgM z_BF44(;MIPX`lMnpY)_B5z%T~A>uTJ+Rfl6qKQ7`ul|+e!{Z_tE33iiNXzmm50#*G{$0;@v(SfP?Bh7KzT;$=34W{ zR95MN9LpulHKamou?&U^mM}B=`)*Ed232*-D#7gjn^^sX3SRq%+yJ}==21HQ1C#aWEi*_+krbD($SKGDO_(25O zjqI-1Own2ra%dF7p|^l*_&hM^SGX2XOV-KUQ<5*BbyQV>lDmnCo;Bq`HSD+B%nY-U zN<~C`ePm}BCVPO8}DN;Y`d#;I!O zu3DTF|3%PO$ZI&eYB-;AmVC{DJjCR^V6QOs*^~)@!eeIyJe=lO4a90D)fr;4S2t=PME$)}H??q8rHKrG(|h{scz| z!@LIpxs=8_hV$By*BZK{En(V1SKI6+h#{j2sJ&8wW(h$t0UW7D)H)e}2|MIklEvo| zkS64i_j8Dcm=cK~@uMO*lC)j)LgiI~n5@k7hY;z7u?r9wAzghnLQ?v#M}?Cnkqao# zUAJLSBRXmAf+@Pqz}c#qHn`Ztx#9f)v~{viwvHpWZT;$YMpGT5jL$$Y2O%ggS{Ez8 z3PdT0Gz?v3FiKdMQV<2-h?thNVU3Whk0KE5rF6;#zN=O0t@p|nYZZ@0EX3gl0_B2ka87EV! z$dsy1)ys;62?!A<3SmDY7RK?&r6`Ls)}5S6tx6#VQ&3-BC`c9AJER z5nvN5mARyhUxy|M7DB_eXTFH#D2|+nFQ6Dpd~oG?PW`?;zYj!7x--;F(gHxeVsI0J z3TC2EXrx&TSr(LaQLeG3B-xfKt`ZKhl`L6lWQy3L-6oZ&w|LQB-!0vdSY1R@ayOYT$l$FIuuO~EJ#qFd$5&W)^X zV<7WnE1gO5)8qpqdrl%*SWN(V{uS|aVr`YTW!v&LoqbXu%9Wtj5Xs>>RSG?-s}em_ zLLw|jpdp>YoYbjMgz}vqar3BtBqAh-X(5C@f)PCAz$yzp`+0L~2=Ede1j#okYT@_tGL1!|I9#HCCX9Aeg7m~v$2a=Z2^&)c`m?UE~zEo z3Gw+cIkxL=ieZ8L&T@r6o@OG-U6Qo+5`qrF^#WN7O3EKmLsJ7wms z+A)_Dbs%W>7@|ANtpF26?KOJ4w=5u-DWi417BgcOHT*(qmOvhiXgd4V8NQnNj=~bT z&A=Hqs+h|*$z>J>G2&#a-c*1#9=~Fe$vfw{$Y7A70oPOjBU@R;`b;L6>Qmt$DrYwe z6>6v#g7;Okjv}G-LMsBFLTj=HbqP^R{tKy=auv&Cpqx{GvC4)CVz3IWDm>tkjL{Sw zP3xn^&6&zx+L4VpRj$4)mP!@-8&*V&f*zKb^Q%p&TCTj2jcSkF^ct4wkO2ENrhbn#oLN|y0Dt8N+Fd`AIfr?_E0vp_Nb`hW>MnOK^s@LqVcUy zP3g=&k4}rw=sZHbSX(t_1oO0|DX;0&^!3d_d7vkmpr-rmn*$5s9P-2vtjchlOIw)T zo76G~xYyCMXq$lKvBU4&TfQs!o@v>HEv(4$QTe#z*gE=2jWT3IZqPU+uw(`SgnFf* zf%mbND|{v#vV9LdFm;SyOHadV8p}uX#BGQwHo^S20I)cO44H1yXZqw?F+Gis;j3PS zy!vsNBE5zguVPaM=hV-Agn^knNsJ)Ds_77)x{lO2hA7x=}uZ3AI?+oEg z+ma6zvuDNQ6lK0ff3vz&@UWBNxf|0hQF6Q6VXssdnyv~g$)K4{E$8F>1SdyNhNC-O z%UrEhz5rLsmHB*=RUUJ_>}J3cCkXVjSaxgGJ$2Ps_hCRdW{`ok8rrfL?Z&Ogf&k`h z=FVhWF8Xww#e3c_l*TdXoxrt9o(_x75L>*>t5`?t^Tsf1L7%rXEr&I_G!PwUb+;Px zKOkc_;DXN(m|v}_X=n=a=olY(?8S{yx+Eh}qqcGiG1GNlqsA#HG=kdDk)N>yLWFb{ zo6AfKK)&K;-Ep?oFdMpY;A|nP^Yuh#pdnzjSk-706EhV!@2LRqr=>}uE8&8~UbVKAX;?N?<6BUK5gZ0X8__)MGG?Dk@BU8fd%Is3o|D$jL zR93LEJ(Xt!A^=to!#H0|Tnq7~_c#oY=ZIMBl1g98MD$&w>UXXgQCv-)M=+u=!s~lG ztQNRX&#>D(fI>eojWU{KW2wUw;n72bz)@K$L1}e|g@{1!y%QIuW$%n^KlIklg4qMA zKh0y=t|SG9d83?fGZLc=U^GW@ZG)yHTvGA*ozDcvBmpm~#O(S&qxf69ar;Ym}D^bx%E!`ivN2gmbM*tERtoNt_e_m#nOr+|GpDsi@H* z3Dh(fG!*im?x5KIB@9WFhw;1@HQl$yZ4HWR;UgQ4?Ek$%3}TC zQ6`lkb~ST!1+Zx->TU%!XN4JEleKG}v1}Y2$HJY2%D6JtU@4YbqC$UllDVyFj$%f6 zps-jR638dZ>}Bnc%yQ4|>_ESn2HZ3bV&VAtmNDsgljYVD;~mVmY+E~B_cXxZbCo6KMw<6&V5muh?8f?`@ZtY71(hmY-$bx}(c2Fr^x zmJlWSErmmk4`;1igoUN+BCNpbpfIJknpNpcn}Z8^%dPWloYAMB5l8&pff7J9Vv$PR zyYhM#fdQqiv(a!L6r9Rz@s@*DMFiDpHvpsxYH(gMaAChf35VSvp>W`di2MWyhz{LY zDiIJadh!ziLD(s{hFRvFsy+zyZY5b13f=L|f^el3G*}GGPXrYUl7o(PKtKfE*8r6L zDSR_=Qj>&0wfEqV0Kji1BJw`+iR8i)0XlOfIY_KOH>esBc~9PlI*yP3o5IsNsjd_I zdSF!mO0&??P*Ega>S5I9$P-v-NEB;sDHXK~Vr6#blxu+vEe*DsAXm>4*$jIqL2Nr1I z-Gr|zMGrZmQP7`R<@cHxaFh!>B4SU$<_u9f-5df@JGg^0H3b@ka1rf@Ds4d*V5tMhJ151MQ?M3YBEhXAe!R^o4b`bX$?csYfb7RUWgsv2xK zpXL?{@DvXL0Kg&TeS;unz?hO(1mmq2sir(}%0%*KLGs&GP;|CP0rS7)JrI!(OF0n{ zh^kIB@&p9v*dvIB=g$k14y=0u@$no_o`8J(1>^x7P=1yws~!L{Mpd}jNy;}EIKr?v zzVyQ+cX@{SeWH4$d$yL}SkS%1Vu;wbb)O^})l`Jh_XhdL%nf4L`|wm18F`f_fA7Ac zwA8_}!X?3~XRBM@_6W;rFe@OcqR?WhQZy-lff*DED=0R!gQhhT*sAla%#I8UMLs0< zXA4)0v$TPAAXNHv&9i+?^E=1cqF%#26wBdB6rNItOcDwYUY7tW=@x!QWDf1Al~*`< z01{fTAtJKWu8kj?S4C4rIz!8v%b1r@FmVG-tD-*3kkJ?zM&BC~&G>63AhV8OR z)wS1taRj2P>eKFj|nw_#o6A zB{C2@dlSY2<}B1!P0ob~>KE!XIYG;f6_f!@wo~D_E}aqpl-+auM1DfIgF6ErJ_v7k z<@I;G`SAX?9zXQ1lN;AICnpmCjeEFvFkHAi-gf8Cqwm>${8J7d{qzGEhxPHaUV|IZ zg_m)#_)n-+r@~##3&w|Ph<{RDnHxl$Q3Q%$5>vz5NDv+-sm2!Oq-<2+%W1sUzA91_ zXMK7FEZmE&J0ufWI~ChlX2q+8jr*P)(-TpgcOWcR{1E>C%>8$uBu8;Lj#u@}?!I{M zy$ib2NvABKEI>ktBtT>nO^ybE$Y2xval|$Tj4|29V1hq@ZA`MU0hu6!5K#e;kx)Vs zLiu$0U3h7CXS#lWbgE9X^G@>T`)i% z03-qcnt3;PvYrC-1T6Y77tTKobq6qNr>8z`?*SpR4tIpuVp10=^^N|JVpLUK^!{Op z=KU|tV|Kwx=pJ&EVz-*1+3>Bi1MKn*!M{E*@72O;3i8o=>K|NYCk-}5q>&adRCigt z5ES>3IpQ*|Vk~vwR;uP+hyjsB0FP*Dq=_%BGHjW{+Kw(9U_%rQt8qzvwA1kaKPeGO zb+M@#B2OA};bD}ONcYu(P~mKv4^c<8i`qmkO#_fLV`rw!X;Y?On=uMGIp`(Pphb|@ zTt>F_IoWGE3U;7(oh>uu71_ioMOG-#d!;(z`X<*2$aE z)Odr=Ws#H{(wH7yg`A*a<;b6KKs}`62qbTCN8-^u5TB`(j^$irjb`9*HWx*r%(Et8 zxz8eC{e3XRfG$e$iWq2Py3 zUU5Xy)s6Sfd79_MQd`?#*uXFLfO805lIE~+^<8?)ub0`H*7(-E0lEj^Xx;EyH1=8J zusvBCa-u%8#H!t>3zKe))w zQbMOE0L1)l^)){_aP6i0Zu`}NrG3?~PqkFJVL&9%D`pT2oSYKtHj3lVn0Ua$*PV3E z8kofXN-c*VN-TuMCNXZtfds%l=2T2VrY~ZZ71M++l?odD56wvbs|QE!fyvW&>L%qU z>GTpgl3I}_aClheb$vupb)>A&Z(>@QK+vG3oVZau<{XL0>oFS*0qC@qp$kMDMLK)0 z6*rk2AbP7gG$by$dSQ!oV$2M>nhaI07K0!rmBB=YY^24#l2n@k-I}Gl;mwQ-@`->C zH>JT+UV3_+s_Vi`xn*wie#4LXGX7AYUYh+X4FOC_gZm1BzGj|Q(gOp}>)@&hjkFjx z(3L$hjPi4=3Z#JC3>l*aL-U>x=ZGr+T(o2FxSs$3V6xqYVO<{?h1OETGh&hfZUsFs zbS_}Den*az8-tMcC>?w`2unAz4#szITto!+yVP9P!{>RmXPWK8)naTJ zARIHHzK~KzdWLg5_?ixb=C*PW2r)v%vIEK*ICGIt*E+v3uQ^+$3x%PYg^<(9F`P)H zkKoTJ*QPIweDP`G%SlG_D*5vr-c+D{>c_q`_+v*zFiA;;l`{6eW=I#NDv#R)d;3l}Yv!Sk-+0P-lQ0H@_Mg0D)Z&#k3P2i&~x(XK=F8%H|q1(nyf-43cWI;Zd@t>MHWL6ziICB$bQt z#B83y-e&*i*U%W6n0f-hDGkkE85hd<%BxE&#L`F>?G(9M(2$vcOqYKDO?aAfF&oTT zva77(4;Y5|RbECQ)k`xyV-uV%F;lTF5>8!UPnsP2`=}({ZjKOG%iFkz(f=-%es3*( zDgaPOq0Tq9nb~CXWUKg&)8bLyGYmYG{6-BMfO|MK=e!Ke5gtN|vMN%07{a}CczifX z^P~%!@A85O+&~BfU)v%MFvr;XzL`tz$*pP>L8xi^YKVLSJ?(oUql+=YS-q}_=EFot zY~LHsq-kierQib6^PZc*`^*&|4Jf1WTk5XBH5h>m^GPE(=};`^CpTI{=MJ>VQO10} zDvf>6p;OMp1nHrm7oIL03IE=p7r;rDhD=tQLSW*t*t$22&PLr)sy>I|Di9l`+F86} zLr&9|i3XF1G{H1XUh+OxReS|xj|=8L_i8Lwvp;I1178;>)d;uo5V@W~?I_Y%1=5To3&%6SEInR!Cue~ga@gE3w=e+d z%^({>Q2scPt#^N+c`so=S%T;IlkFL!JCFbfD1a&|N&$rb^R2z#`@)V}ez~-?SQkB< zm@NuK!Wu}{1W@yxtq~-4I@syr_(WG$<@_#r>9_XZ@U#AdE?e`&^Eb{NSq=s@)QAE^ z0q#ww<1rTc1Ca`-?SR5~4GkaI5h`Yh_p~+5$s{9`C!Z9S$S<+5*k-?~V(9y1q)$8P zdJrgt5dLuzx%uJUnUjvZEJ4osBRj1CfQag~G9n_N-dhLEiYU%3=36{#Y0Lw|{G4iJ zupo%@>Q3X^%6bQGVihqpK51Dr76Kk;u{tO zB+j|vd@z9eVRC4|7Ajd!hAI1`?aK;CJaFG%3_H&NbXGCwA!r;(t8!!^F)umy3yskO z<2>ZLP9O~#Zi(;5j~aZz1_CzAV2o8uk8@--?s6n~o(q0Zf%_r8D?~As*Z|evVLBiM z8CZQ^FCDJ z9*Jy=6OcG*=D;<$+LordOo7ij>Q11EjNB(WX$;*1xl8NU<}jf_ev^X0o`PjPYhldb z?6BG%`4X#2M9dGoDPr}pbybq`mBgg^(Y?VcruIlJ#GAJ%BWh+AE9G3}7%g32{UW`^ za+7IkF#5pX@i%v?y|Pv*2lpw2-+RjcwOb~SdVQ8k#bPQ0iUM@V7u#_IqoJ?X`7oK^r;NRtWfBcOJhsu;R9CK(;FvBr@0B>UZ$R?gdc6Y}lRE+mia=3uWXDQu?TQ z>XR9t_Ie@Btn9G~t!!MSZmVwM`c_|f98K!99fG=|vMc)g=)3=X&n5r9eeX`}jdkYM zcL`)&OSS2aKGBHy7fGOx5SIXgimq5Q-jy{i?vYW zAfvVT4tq3n+JYcHJCVU25wR#-4$ZBm44fqn7NZ*IUj(DG(hX!b9%l8`lt@GJXc!4< z#iKLz5rL_5PwFzhPJ_wUovU-hY<=SjC=pMvfZjbC?G@Fvtf~qT%CZE2x~}WGMuDB8 z4BIlS+NdxhvpAJ!b6Ac84X)ZvF^N0X1rM1#valsi@i%oARXck=9RtL<*O+Elk2#q= zPAW$KCK_LZ5hRgq@qlnwC)ouwdbE^Xg^?=(0u4fv@Tzkd`jE5QJT4={X?`6D%cyii zs}kGCHtze#G7^aI=~3Wf#eR7U7c0#<(Y+*ANv^QQyj5`J8vEXR4xiN$*&vg112OY6 zra+TnirI{#Ju-n91B~v)Ry-^CNwnK|XlZB|t?o;v@scEC85uSUS)60D+!UyUuWq^2 z$8a1Tkw(Il``OG4BZ{5*0I?WJbCV5Yq=qtDEWRJaSKdR05J2{$);3N%ElLHWQ z;OZ5onRw&a*q_$k2G%DF=spgwklEt@r7kiZXI3F4ZBzSn+yBerT;Dv~+ec&s6aMNI*^}nXYp9o_tY?hDNPj5j z5kg{u(x7shI+rp}Oj#1L2NnVJ`+r4yy;{XT5jj0LH0=eD1R_~i&>Ju2?-~BrCvLs` z!acGcn_W9bM0H&ukPrZbq;n$#NT}F{)V4Dc06+*pKqOgLLg36yQP<@SS1$bHpWpPn zHy?4@Z_N?`)%xokNWEu^-Dyq9~$==u#C{%pePD8 z@D>5QYpmLqWtnJV&t>#=4H1=P(dm=`Af=Rb-R*W&^Ofb5LLUz2NioS0uV~Bz007X( z)thu7np4$?06+rCN+Jp&PA^V?(%AtGjj5lkSTpNL=-agM+O^IO>=c(;SDi7f{R zsMQcWwi!M+KCy9V8d|=gH=m|jf{Sv_36Uo<7M}nhTjInyR{!g_a$=qe_0z+Nk!+b} z&FSq%|0)f?LI3CGWfjGD1(QIzPHR*TI)r^e^=zz<5)Ns1UpN#Psm zlhEZz%?0uc)^B=(R+XR(@bMs|Tnnx$V{OSD8bI$u15VjC4B4nB6H{;Fo1m4SS;OF7 zK3g4(iKFJ>88pm^RYN=y*BP1ivZpjM{o|vE+UT@rhUQL_)}K>PA=4&h(-5<-ihBH} z;5)Q|nLi`-oKc-5n@27g%y=je_+ z3j2ly@qUYg=&i*zY^-w5Wjy#9f+iAEaE(l1I&!ZtH*HJ-@!>-*yzm*z}s7q*_9S zh1#YCPym`_fyz(-0tmo@us~6$n?isPC=ju#C7_&JH*wG1)jz%KmS22rH%bK5M|}rq zDF;A>Hbz5Hh7(&*o@HUx*C{$C=PLjhfj#HjEPe8)IK4f1I0oM-&^=qv0TB>|$PU1I z^*$gqwJmvG0DwR&1R#RREJWUaaMRglh}|!8k6J4QGCRyh$~9<>{+}33vPh7UWFR9_ z%3iP6>-8XIQ~i_y07xl8mNs!gehE(`CiYV;I$H&XEkhKI*$yryV zbo4>QPN&oB^}5||Q5MVUg;n&gi?ub^u`WFNtS9v*om$yD|gv9;YFDn5Dob z3Xc?`mjdpx5yn9{3$4v%Fz`lfnprNe{6TgMTKj|tVLA`G~G<4^mDW$Vy11iuNGYGEg= zPMS*k#lWb#4)Mr%$Ku~tYzHRc~|MoXO zzHsO4>`b>?4(p+s71bFaAVCcvRK9`$5eg{GH(8#5*j>=;TGkTh)=uxO8^-!WsQ)tW(yrl+qUtZvAXaK-1@gY;qdF}R8?10JugeU-rq%=@v`ushW?;tY5 zpxpxV7#xoL7ujxr-0mr6f>aV(FR6$+^)YbKWE8?t)VaNu_EL3#MIl5XL?P@)SQ)36 zQA`uWo`lW@O;O{FJ_G=}xQY+MX&?YDpsrOUQ0jGLvTrIY|5L{XH58km$)eQDL}^_Ewb z-~Yi6eDtFqJL`cD{M^5O9ufcLpFj8B_r31{_dnx(A9(*s_d97g928{%4VsYjCtU$? zg7XK=C_y1WL}jN_SMr+cZrHT(kPYkC4f+F7pneL3fND&q=lJ5qhAzK@5A=%y&qw_Gy zJ3hSYRlU#xpOG3mRgxX1-TguNhdN3b}aOxXH(XwBm=U=jReQvee^7T4GYxi+(q z4H=9O4IfT01UtqAJNR%&f2Pc+$}$L~XvX9#?XEDaMl(!Ox5tm{oumk-$HpGz`7jM9 z2@nEyF^-j^6`wrjQA3KC0;R-M4H}o+Svo#P0iKbL@`o2XI6?D37X?G+PFIX)7)BZ1 zaOtUP*}Cx}#X zDnF=H zR4f~XUVox)x-GE3O%DR2X-S}OeX+=uQK_}j?+uruJ>#w^W5v!`It6Jq{ zlHP;T`)YO0WtU(2iu2EZ%Uj-Z#T8eSWm(s?@;N%4PN&lm0zqoz5m5-y?RHV%w=eqk z2S4zEdN_RiW1lcRJyjIN{Z2T3c4q3Li!S=$2R|qfi9ps8Xyh4eAtj{x080{q+S^nu zy^#cx2t-lr+q>_R|M;m_z4Dds{L8<*{r206q5vR;TOow~46Uwf_4f~d_`^56?zMmQ z&UfCubt{-XfE4M>_D{YE$onpt=?DhN{NV-@?~8;tH3<~jX9+AA4{VF#Ol6#Z3&7@0 z&8R-YVT3mt{8f%<(yS*#ofrXMBHZ6>E*bzdL0;9P)y`-RcDrh^o13cCZsen?StmSE zc)$4|fr830v|P9n+dCp4u8ILNL>o!TgwHdo2|$a9Zmy8?g<=E_+WTtT@Sxx~1CKLE z`6iy&M)ADO|o z`ZSQ5QTD}}E8;BYVSA=@)k@C=Re7s+tLNA|2MCblVD^J^v%1AvBi z3r@P?IwRAK_OWIW)0yp9=aX$SuDnfxa1?!8Ic+1|&4#BIleD?Zn6lMjkbj;`GoimK zsv%3&kmPR2QuACd1%VD{0M$hQ?W8`n*@mHtdP^*%973m8Uj5wz7k}j*Ip|ET=?v>3 zA_*!;?YoQS%pU}lqA1FOU~zG|zg&%XQH<3fK!^@P0n!BZ>YEV)DTyRiBuT=|%;eQS z*?;N3&p+jLo5UE%8VUjufC9)IRmFyqVFgsgaieAz1mK?`r4~J?FI1$;7fGR8q`xz< zn-tk;a6KzuIp#k!D4@r4&Oh@xvH5cuog<98jr_EFaPq-7k~G=0I+WD+A|+` zW?f4_tgHIK!UCdLGc{F~1=KpI5JD71aocUT|JA$xYGrBpm9Ko&o8J5;6o80NdFoU9 z!@&z*_@ZxI_>Bwy>l;sd+EW%57rSMNQ1DSWWp1ddv6IqKO{H5Tv6ZH}ltt%PH~#9~ z?|#>|t@r%&ip!2U`j|JpxZLKk*UruJ4FxMjgIR? zkCHema9~YfN{a@@G^c6xn`5+at>Ye$#Z^h4L~7Q78w}BZVsJ31tH*Y=4(hXqnHV-} zaPX&Jw{>D3n@tNDxYmoq57#1rK%jS;5e%?R8?b~UX?;u|ee|C;WR<)oc`vX%@+0S> zxpEr|aG#VGIxDoRHnz#ZY424WG*;Q7xth5VoV5voKmrngtU(lbV4J-7%UgHdSxrxj z>(vXgAc-WEa0vn-KonGV%5pfUcg-)2uPM%Z{-ziF#ZfPM`_a#N>(NJ?GC8b<1QJk6 zqMD$VP)m|h){;QdU^pmx00mz0-+ONR#ZoB*NsvS&!7LYWzC(6Ub^u7Rw(;AOrU0qW z(>%rD76hd`qNJW0Q9=RIGGr~T+IWbPz#S0GA!d%6q6Sp#QcAUd4}ws>0a_JJ20=3a zT8A=|gDI3 z|A+tQ|6G6F^#riExVW;iG8_(t5OrOD?~+Td{P~q9o_N2P{?1E8fy3cYEsc5HV;=XU zCq8lK&K;lm^#2_Wt6sN@LKKMTZwaSx(2LsaCO&GrD)^#lkF)=>5 zX3b2eQ_5Nnhr_{eSk<+ZdO?#7<;FgBMQzC89 zi=3{YJK#R_s#JB#scQP!RN@3iyZG66Pg{4F0@YlhA*a)(c}pzV->JtY*%4rs?7XGf z9Yts7i;c|1WPs-a<1Cz@W>DX(&rPD*WLesHG9yQH5EXI^(AR1FmpA-t^t6*CXA}` z`$|fi$bwDIpYiS+E3tk}C4ZXyce4t604$Z-iBV&|uaAw(hnqxFP(zNEdE2LQ;}F1Z zVjLgGm@>APbsRond)e3~c=J|OY$Y>Xjn4W*#z$^B(MhfNh$5y~nCjk>T zORBTet3jh7Uz@Aepi|<>TCIMG`%PoQvrF+1yntLEDrH0oB#^8{Z(Q7P>HG~pJJ1!K zPN%FZDFmo<;glapg4F5{N%h`^#ZISqz@yha;u-5te#m4wq2b?l!@`X}t;?}OB1j1; zStChbAR!8>U@*O|yyMp8|M~9z!%rSB*PyIP6k41lfN)c5N2;VEAe~;H7bPsgNTg2* z(g7n%H74g&rKW`Z`@v;W?-(~oH&yeZ-gXI>RACw@w_wM<%Hd1~U|fokT!d*Q0|X!g zt;1^}5|F>KgZfLqregPvM)1)P0BFCfNBa#H_z|yupU8*N81ls>QB6RB1rbe6PCxfK z&nZQ5#9@a&_32MnX$~S94u?b}giwhR)u~X&M-`$F1rbe8PoMwN^QXqA)~#Lt)Mq>u zhzLnay6(E`zWI%Bteu&A=F@)rq?1oPaNt0%*Bcuf>-Bm&cJBQ0m%kuToOaq7XP$M| z^3t*>5TK}QIXyk~y4St#+ZTQNs;hr-+2xl#^4xQ)swzq$rSAj-sB9Aep$VPh{PQIjU$VH= zf5Hkfi1ZjUifg2q%ICpFv!$ zm9`TG_n65og&sT-kO=a%+G#Ps9peO6!_V6A8(EdlD#s;ZluaZLjQ<^0=zXEU89eKg zvY7CCY72uW_=qeEU zgzj(Swk3w#5beiiJ!$C8LfN!qGxFIkJa{UDFs8ncVrHL1(LG4XZK#Jb z=qMyH8at>KNd!lELw1E07&I87ESs69b2GOGa~FoFxoOX6p0u1ol7%Ao9eu&@>RVOP zD3Spwa@k(gCnUOBtk0Y#3?O?wdgj{KGls_!nv<89KCKmU5}K_Lhbs0_e8-!$s7n;*q-Yy2p-amk-KIQC}DRO#$btk#VNbGi!*9eozmPlE6# zX&=~*ivXF!HIPlCngz4^%p9f6C_`G4m3i*Nc=cMo@Kp>-=9=1a&Lcn89LYKkxy=6C z0lx;qmYnaL0Ym@VvP__@gYFwVTwb{69`QsBHB_mqGP<0h56)D__$1*R1n`kf+N;Z^ z(M9Pt-mDn`AC79mCP@NG5CWF>(N&l3S=>8VGu;DGK`m64MFMK`K-dwYrfNm@k3MDW zAy3+P|3|H#J6r%zT>&C2FIUSeD|Lk=h1w28sy{l3gvv1@9G0DqMEd1V_de*3*`v=K zTUo3DpwQaHb4cwVO%{e{LQtKz?MIOTLn7GiqfIi)lEIUE-lReNomPoe9Rk>`#|eVZ zjKKKX?N}`5_PUE!^G-^h^}Z|?rQ4tI|c^FO>HAH2hXJP zXNdxxuH^-l&L*`6s;a8vk2~QH|L~6t+SKiK?Gcj0;jk!*@$qpBWH9L0wG;w@psMQo z-S5Od`-{H-fLhA_%F0-;SC(a`Ta;zFe#82ix#{uo@pbFgN=X6{0Is?2+8_S#hZ_#r z@R-Lww(NA4msbiXgpi1cMS13#XFl?g=YIeDKltuN-+kn{=MoY3FswExilWo$DD;So z7bmGGMXxsj0R4VlOKEo;E{bw&tOEd5T_M239{R9{J@jD!P}Pzo3B+~l<~AR;X<~e0 z-MZPeb8CrcFdP8kU@)kuCX&P9us;}-opQs5^>+18e=w-3sw_*D|FUUE_FZJcvmaSv zA0%*=Sfm|sOfzKU&Am+vD+eOFm2uEtC=OS>%hx zX})BFZ&mrJBx|h}2yY0fd5>!b7Y+C%+v|T_vynP1IrYGV2He>Xp7m9CQhl0$c2u?{ zC^^Bjkb~eX1fFqmLz6M8aQ$x)89nI-k%RE!?MxGEs#ODr8Bv3Z``aRLudHLxTM}Ai2x8ZR*Tj)}b<{9ZyqDJ`ar1@}<+*Z3 zJ}qKFRMcSPvMgxd=4IQH{-ZaX>G} z_If&jC|A22n>4+1riihWH%e=Pl9@>GXF9$abm5^JjU;hR6wfj5rHM0Cov5z{FEGZO zd=9qF_)SF|+g%JiDX3_5TaswLj*b(q>SjY4sJ+7xXLe&e(_KVvQ&>5dPt>fhS^*MJMQS}(&R=ekSo>l{mx$dj8|?s=FGAFO1*#g zpgUf4x+n@k6%PAY)gTE7NCE+qTO~k35CIa&$u)TUoeQ^Kvv~CBJuJv77&N2td4X^4 zYmX+^<`F_Za`Zs{m)dPsOSrK#$n&zEA7k|ig6oizIi4)u+;{N^K&KWkm)Q0Tw}e)A z5dcQ2tQm?@GIk9jfm*EOlpUZARtbOy6W~avSq{i0=-fa(q%By@w&)Qx;92%0fJfx5 zS0X9hO``-zuf8F}RQeQv-FtT5bN9BXsj1C}9op@75pmb9-M_l=SDQ9$Jnn?!h~O7j zU48SfZ$^M)k2&s)GfwSx%jKnIwXyX4-o3xM^;TKSBac3MdTMh2f&F{t=Wo9GrmCtI zmlkih`TD83S(0?rkw>nbn;Q&=S6=yx<)xJeobiAsJmK+mU8_^5>bfq3khNU1X8Q4u zfBb)6^quRkzg}%lp_bWrs=k&x=l9%x=bdHIIr^w0r>3U#?#_Vo^Lwwq;fBeH$>Wbd zw$~jaB9MRuE-WtHdFS2Z6TL$Y+0g5Bw{E>>VPSD*W@g>mwPmNXZTt3{etpx*;~Px7#6*m6W0=#wW)2?%VgHAOB?E{{4l(Er)G+@HuC9yPd&c zSeB)fQlM~C#+77V=|xINwX#Y8V}Ce|0yGdWD{56B+a%1X^Eie{>Tq6%zprvCCzGuxMuTC{%1KCr!sq?w;-$1lILb{-*v-sq2A6ZgkPLD39vAd!8e9iSifr4ci-rI- zHCe_+7?Z~}j;YW2B-}5y;@MOdLox`Q>l&~?tIFdp zav2<(+RJ&c_9thj_KeZ|C@DDYCu?LvhdYpAP4h_m4FGl@LXJ|vLJd7~8D)0@GA3A> zcWP`pq!`01fsf7Zdu;)Lye}NG3N`@G@Xx6+PAmJ)JM?v9JQ=uzX^6V;#_J6>pQ-Vn zTZ1vm=)|4!VkfQ{&ty~O#Rh7eC8-p4S#FIyC<=JJrh3SvV|K*{an$TUvBR?!&fzW%LO?O%H0%-A7V4G9GZ5S-n)%&CPn zM(`L_xc;|3jO*N(V<4+wEMOvY_K)Wq7)=(ss@D`{h;T+;00Iy z{O4cz;+O8c^Nwz(bL=t4KI56c{i+LIxpv*!m6esTv9Vv@a?6|F{H9)a{Oxal`vV?u z#%DhLnG3)9joWUy1ps#L+V%eTeQ0v(Q{!V}uX*)rUi#9P?%%)f=3oB`08TjJq{9w7 zY&aYu7KlJn)&u}`r`tXL_!Cfwd$!$u$6a?Fxn)Z=tR+cnKoAj8T>Z;m{^_6n*;uFd zn%BJgg)e-;;$pwk?GV-9|NbR!|I2r-TRZovS6}dg=f7xWX{qdVsFvUU#^&6LjcOJz*2W`6>$AS^H@3YbzdpFqepnBAF z6}=}Bi0ecq&|ODeqRl^~(KP{f)NcZfT#7KRmP=MPlw54n;5P<$npuuBIwli=DfrFV zRR=R=M=Lx>sZx79L_mO_`v0+EvApw0|RQaF}v5mioi z)sUP|;osmbg}#nKJMIwx!kE}j2^T^FQNmzB-uCN-!IGRE?;()@fgmJG# zyKY%pTBtV6bnd!-ui}WkLJ=x8uz8p!*(AM17)b!V^>uoT29S|6tzOlrEv1lg40_%0U6ie^582U> z-{?y__y&KF;i_7N+wl;9`szvukQJznhEX5_?%2NViYqSLy?f`L`FQ{sR@Lpd-}|8<{5QAUymoHgmMw?hb=TIP{_Lk$U-Qd_g#&;5M}Mr=_3YU*f9X#yot>FI zuzyh!-EhNoKlt7y3Wwovc>8U)RaHGRJu^SQ7XbF{+jrBAHx@;C)X_&GqLfky;|T%P zBs&0G4nJ(e`VBj_@4WizUmm&T2)#eJB%|19d}3mJV)EAC+_HP;uFab_zwm`GsOzd% zjP0G@d*Roz?(@=YkO3ZgD-ubR~y>sXG9h)|7 zI`W9aZ@=x1Yp%KaS2tc?^@o4<=Wi>D!a8G$s!nk+V;W36Q*PR~X8HcKFRSH8g(W;+ zYqTJD**!jK4E60St?W?vVkTBIk zPGJRre4XPdyFeYqCH=b9CO)ESd{9Zl+9m*FsK&GA)Pp}9h}Y)mZ!=ps4Xp!-Y@y5& z6|YXd)6g;S@2l!T>r_X|v!aG?TLQdT0-6J{04#*QSFN#GPAk0}QXHynt~3c#LEy6quZcFxW4(A51fjh!cav-w>NGP<`?otP zE9TD#NKywj`7b6wut0RYqR7mtZ8=UFiLXg$CH)uV2HeP(1zGysL$(g2qYLY>NN4iWiIO-cyK%b^+WP(X^}Q+qmX4l zQbdefF=q4dZ_dKRri!~XZg`e)BU%M*o3hUo!_!Xyz-n?+jkJD7lL4^q2&P_<3V{}f z(B<~gNQx8cO+y8s-9_BhBmn+xg%&gh(}q`!y8)n#QY#@L+&ywv(L{p&J%X1!fE>s{ zq~DS(-o|xkbaW!yU#v}@B8rGeEFuS0o1Az-g#i7RRiaVP{p?BB4sJP z`yS8bVaBd@8{{n?yBkRy9a)1Mhx`7hEMZ}>p5N6MfZdJ&AP|TI0*Httfr`0gO$4GW zdIxsmt-ly-y>|HvAKog8&THRu_}oTO4?z;tl4MQ#NFCH$*Qm405eQ6*gMf+>$9l!$ z{^5c7;h``giXZ`*GT2T?jk-)zhE;cRfdE86NC*JZ?t#OJ#XV%df9EQY0p^qxtBPQD z6VCg!iY_>U$5K=4?0@q%9ujmSN2&~hIxY%;2ql3q-%$|(sI7F+j-C+xBh+oT%tK>d ztX~K;XnV$3w9R!mC}h8^M`ks6P8As12_YoaHfah3C~$gusyo)5n_H_ktU-XWvGGpX z*}r%HzTNZBd+rM^xZo8NQ&Ttm>c)@#!^eJl`Q=~u+`pdp=*K+fG3TkQ$Jlspc6N5W z*Bcujd+E#0f9S&={+<8+&KJM<#dT}fzw&}toP7FeB^GBr=s^H*;J|^KZ~XPt#KgLF zYr$7{M5JiGVZ+9gPdWL2e*Qmq-hL;5iY=8drDa@7dA}3x_oOF1=?nk*`CtF~<{dkB zOixc2qPS=4wjccPN8N6>uIt-wyLIcayS@* z1Sw(v{sY~WmGfVE{_|h>yveDltFF5G??3YQzxve;pZk}8e(F=7diH}KRM#~kN|FFj z=tJ49%uzHIZDZb)hh+Hy&Ez{}+0wAGcq8F5j&uQATki%J*5LwxR)0rFlqTblRWD<) zZldt248XR3OK+2$!6ss`g4SbX9^!KJPVQ(!kWI9&@!v$Xz-lc9UyUJvne^l+l3?SK8hBQgo2^!^LfpAI`@E$wO$1_@gb2p1*iAGZ zp{r#oLsU70lSdDft+yxzdYeVSX^k2KOcE!VN)8f&Bm9ckKCOPdP4K<&M0z|a3Y&;6eASie(gQ{A9b90ALfs%lv$_gKwA!Z) z&XW`SjG#B@eFVMcMCVh8yaa93h1EM@SHY|s2;_H{e5(*ENKXiTX43b~BhO0mt_27o z7Wb_j*gNbLMcFB8DN!N_(A*~pK!1ZV9MV{C{J=K)kH6cxfA`?}s}|0F69oW>0tkesH^~f&5oNT~ z?i0+NbSDAaVOYfUn_Z7%X^fG|u-Xc30*B0B?3@GMaXU=YY#Dv@#aJl4&XM%6Build zkpx*t^+g+*rUdS-yu(1G5l8#4usjv2Eg}ZTk&2K}E5w!&F&X6=nKi1HW2AJI1mkd5 zbzKkZx~_nvf+?jOR#mrCKJ%H+e&72)aOmbu0C4u%XBPrrdDT@53rjcMc++DZa~=>> z!(o4=FKd!i^*Y@LpZ$EU#ihOb_O6+j znx3B4R1-q&o?}s*oSZ!L&`m4LOY`%40D#nKCWZ<^h-z5R&a6525$AsHpa12aty_P3 z`4vxo>XZBZyy(zk+OIzZl<<>Xwf=K-D4)jAFi@mR=H{4<`b20wRYVWxTXDG_ny31&9S@RIU({ zbKybo8|WdmarvT2Ogan}2O>Twm#^cMEu4`8$U~-F@4-_ZhVdr3RylwpL#Zszs(ExL zE?ksGA|`~eaucDdteKK>ULhITFjeSghYPNThS5QL45biM5_u!07A!LIkl#CBkCW6H zIA2a%DixSO<1#-Kkd3Rn`;O<@(dsI zWGS`IfX!yC>)eb4C1rLUbz^=uVR!O=NFu(>CmK<+)n0``Uv2IQd48p6I;5h$a`p;ZY*w*FKtco+$h1-;CkBU10rLmhUS46>%HxRHna>$hk=FCekt;DeqJ#tcx^HV6MJ5nAt3-P?jJ537!(48 z5QHcMfB>=!?3Cn6swEYL=r0d%zj5DzdAV-=-#55-s25=@9A=NO&rwUh@A9Ju_7%K_oUlTJSM)YAa~P{>M4UM(dN)pfml_wJp$ z_5i?v<%PX__wAjZ-?e*pe`O#EY9avIxM}nAp8LFs$?^I5z5Dm?uj~5Zk9hcDhaa}m z?=LPcD72-N8OZx!Aa0VRJMNUqhg)0uB4eYaWe1F4{&X%&a$TatM1hbIwBomQP&IgKSZ_ELTh(kX zG4X9V2u4ZRRzE?OBSnl-RLAqb;!*IfI7xj^xe<920@**jhvv4X_i&vzIVVEju?V^v z8h&1mM)e#kk|Yhmg~P~Ih@wwWjrFeWi@jmnu1cAthO=rZYoGF_K4e# zq(7Pfyg)<0qtjA?-%1R<@LWOWy(AdDF=v3fdCaXiiM;WGiM?Xf zbZzwyndxt%?jzO->6L2LE`1+1@tk9lsuTQfDZsg6r#|i?L5Z9a zjSBILcAia=>KKOQX_)aS$T)uqpn=5;W+aC#0-)aTW@Wh^tkgw;g#d<>K~U|p3n&l( z>Y`iE%yy=xOM-qahh|bqN&rG2Z#9+x)Y&0HhyoCW03kpWqAW!%<el}%K+Oe|RG^J#60WM?VLL}6mi<#g)m?cI%Q`!DRUB_b;aG4=YKxtLb z+toi9Gjs86oC2gipHW#If%Xh*Mgf6{2*Mz4)nGopK-U>Fd+aINebva$XC_q&o{T%c zAsN%!iw2jO*C&u9rOE3^DTl)f0H&sKcenFi2bPzrF0fX#yzgdmkbW)k@nB#S?{YV|}i*E2#TN6aH~PYphvVn)oqP-*W$ z8n8}BuvYEtS30C2%1A+I*-hblk_DTUaQD>c%|v|A;J)$u?RA&MKjG7G>yR|oer9J? zGs015iD7`8cOzG`MX~XE7-MkOWmq;$fochP27rviz=E7rn4Ksna?!YX5~h1|6Xfdz zpSF!^Jjoa%T4fAJV(=VDySzv)P_(q=soNN{EY1$ihDX+*Y9e!02Ihm5RHr$0L_QCu zIwtppOCXO7jdbjT#tC3Nn|eyA(*ucY0q@jagyPi@6>+@O9~o6X82$`; zaA^f#sCu*JPxJ^40gf>+CSZ`K;EjCD004x*Q-y>UM#)|r ztH3GDmB6YQrKvKyRZ_t;J0_6dBdk21``{cz|l z=$t$fGf6^s)5joJIhnspCRuo8YgHWbI_^SO+19iZ84QZ`k?0~ytGZ74ZFZAe<0jct z`YXW1yrJ+aQvinlAONU`wXCH@mt1$oRS^ULC<%qi;7EbBz*?Z>7S?>dr}8rrupqOP z0|E7k$;xsd0HP#W4JAZ;WMrm+r7Vsv5rOs}WC@p`df<^#qDov)$6D$~2$=xP`B}v7 z#VSxEnBd{4V~@=;Xpyh;;{E=H1sqptRa0$JB}JyiD!AzWUF014`<1_@Ltsn_t$H%Cww1pt(V zP&Y*(2!V)Xtv+Uwh*;P4^72xQwd!SQY3c5*clUao@$m^@nn4JmvWh6Ms_VJg*+-oF zs4sr;%Xe+P`^I10xU{%(>#u)(`sw$7-t(Tfb?ZIT)6>7c`R41dy>7!H8*ly1Z`N;E zch*@CRD>atbycY)asVJfBB7LZUDp6m*ELaXJPip%Qc6H93Q?AwvMjZ>AQC9bq7*{t zNr}~5G%H?kkdEKtSqfJ{Gd1ts%*RNOWE^`-adWDb_4ndqR%|Ge&^QU#4O21@Q6S0Q zv8ieo2Y4#kj3aBB)5vG$3j)>0GV9G!hIxQ-;WTMe+4|xQ&QE*i#Agrhft_{50YqWe z5?E}M8xa_3RM2d2nZS)X6%GOOC?R$M>V|@C+$~6PZ!&Xbt`k%WQUEsahKT+R)0&Se zo0^a06AX|GIt_%JT@d4NPwWG8lM!LwG&#@A{mW*2Vj>$R;&@RoJDzcOaMJoZ_OI;^ zMEjO79%IM`mg!m!^e-> z0|^2hf7FOrlO~!Z(c^6!aBI_?TN9o=$uL-8dII=O-KAzN ztEbiD@TuRys*(~`2_%Q5*n5;m!~ps3L7I!0P0~sUFx(1)$ZTYhEH;#N^J23mJPH70 z4Tu1sY?jx2I@TmTz;xp@88o44>Jtyn+4rUSJWsQUO0;rRS&eug`tc>7-ArS^V59d{ z6h7WWr;R2&M7sq6M@a=#F_$ z8YeWQ1Sw|+q#&s`!XdDqE$MUa^sFLDAq$WNRtOcEqmoeNZ4lH(yjq%3Lm`sjor+CB zvJlYisV!MOf}3&a9d;mOC0{~yZFG;2)BsWhQPpH>7M$wQ7wEpw;wfX+6K_g>1v?G| zuWe>tB&(k5MZK6Oo%mvBLIWbI6(|5e=JOdu0Fs3$ilWr5QK)2&njy5u-P#WI6ed$B zR?^Wv=g)|rxTVs1X7q_v7~A2>Nob03UDHT5lSMe3?2;u#4gx@-7AgruM5;*#kdFzGZT9a{c;s z7NGNz5eS5&y4UTV{h$ZU&d#o^tXzH7HS_!C0pPS#Pu;TRFd})@*=Jw=lS?nZ?6Tue zJYjKR;fzyHJNcATbVr1Btx`H_91;oZT1u(`psK3juqw)8I2@K`DG-aI5J+VqI%Nrn z`ZTekC<}E)mJl`r80uTAY}AqG?01u{37zcVNN!{1Vs3H`BAD?Dh4l7~snTRFyskOY z2GIaJTSUt19cj@L7#OXUYm&ShyuFD1Y$8IwrClm#u`2UFa!}2V8OOF|kL?4eRg^f7 z0A9S94!_JJcW7oDYFqezlt$zPhl7ITEO>sY(bHBUIPil%sEco+*eJ&aqh`^|W#UFe zL8~uqUMr&KOL&cgR_OoKwb*7%$)Jwkv!*HKO0RZmJj59SR!Mrkwz0h&u|T7ed4x6t zf=H2f31HQQu4d1@;yL_;-%kD}JKA~5^Q8YsUyFpN$7vGu-ayrO7p?FVyj2y;)j@(I znTTACPD|biWyd^f&qX0~DoEyj_<%vS$_z%T@LZxNjZi8r(oD(bEL&2a5K(RewFp$~ zV~&cLqSEwgM4NKrhl?A@O4BW(Mu3kM=axvO=%&8;Pi9Xez`8%T@P-nbQPltvbu?aBtau!T03`GV%Id5v)r$bAEq>+{E#(>@0DvSA_Rw;@ z&1Y3pS-{kE2?B@-y48m~LEcJpDIZ@cBTd+yn;s@78K@12Aw%aVw0 zy6L8!J9nOT+NsALd#pmv?X9SRs(q{I@WT&(#JT5w`&-}o>es)zW9Rl>w|mx^X9D2d z+}wGOIq#B-FaFxszP|CNhZI7bddjJrH*X#c24z`FS?N!}NPQHha(3=0rcS3T1mM_M zZ)~iph5`Wy%c2yb2n_)d02U%oS3_H#gXVwd-k33k9D*Z1&x!n$0Q;6oGa6wu=WU|> z7Opng;4YbJRKLS?NwOno;^8DRW;gi|8y=W>3rzJsY1uKQUEZLMkEb`Zd?3-_X4nVe zOq-KaxqXi@2LCBVy@&IHCchn(NGQ z2sqa;Oo3ZQ)B|@Hfi+?UJweiU@@|#82;81nijx6YoJou@3ejjtdNzczqa9t}qIr5L z*@Xo)3i-Sa2~3l#%+r0Dv4kF(wLGT2g+SJ>@pKGgf`&2MM{XmRxx{#axela_cgj;9 zAvqu%eh|Ah22f7>F`UEcVy)uuYCTTNb<}Jc8W869b37Br@G0?8$hXRZ?I?#WuNm8{z>;Eyg;tqu^ky@# zW=T9_<(#O)jheD`zG}jlMtQz64lED8AhjpievyH`8uO45n`lMVb^5fs{m2dMC7_`fjKw$`XYTLY+~KAP9w8 zvI0T?2-JT#@<8Mfov9Ez0z*;v%u$5V1^s)YbYn*O^3td%OiBhc=Kz4(IJ!1X4atZA zQ55F0Kw%*$Yi9KX0zj|V1EBuO%C4O|%CcO)e%?JF!^JzjtP4W^Q)2*X?yW9Z?7pLX?FbvSCuaqYy$-mcS=FL4M~fkG3o! zyJPf+cNAI;VcZr0Ak>GOjiwM%%tNfF5vcNXEyF=XoA-n*GX}3kMu`tzjo%j{znU9J zYkNf|CBbhc>a50bY3tKB1S+YU7+o}B(BOM8I8hQUlhd*8U+|iWLVZn!r}dmi75ti> z=QxrP0}tInjsn_Tr-4NMZie&|Jk)MqCb=yc<<|+^XL7AxR`zNXNo)?m_PIyqX zI+pf(@zN~(ZmhG|!+=c*%*&oA0~l!SCO4Oy7nmQHx_j6RitIs6A*GTE`@{tLE= z{+HzrwRp!?C$v~zzA)`ArS*~T1Y-=DX_-Vq)%I@C4P7w{pJQd_KwIJ->wWH}V+Jy_;F*!)a(wXK`%olbnbYV6;6)Oy_G`u&DE=N(B>>YHz-B@}h*=KFhzeLV&U? zL47Ty41&P2EQ>-ITZu}F1!7T%q7XK(B~X-QDT+e5jJ1@aD7I|ba>&LFJGO8C(!YIa zcHL}U)f+c%Jp1gkXJ%#}^{Dgy=`;Uy!wolFf87mdoPPSCKPc?3<3#g&_Fndr%Mitp zTaKQdp6>ToRDMkrNfLFUmFlV*8yh?8%(Et^r-y@Kr`vttnP;7R^2y7~%VT3>M;&?8 z{Z2ahf3Ezwlyt-qM?UOf4_jVduBF--8c=^)rH=?8N#kSVhi=+b*Y$61x%oZs`RfOr zea_1A(wS$S^`NuPLX}nWoooU^6hahs^Ia)rv}}jT7t0hRUXr$OpmojEQ$)5Nfdx0V z1qGEybx)9Gss=ZUNr5L7!~lY&Lmc|p7QP8j_2}d}KN7!Pg2IYy&i)~*#{ET1p{k#< zN1oeY2a-ZPxEpurh;P|5sI4hV+$1%EjZ4gVQmr7V>52z`5jNwwu^rn{LED~k6Ah8nI|*} zMN^qX!{c`IAh@4E%qBr>kNL4A)tYfEc_$QCq3|0eZ!C6NxU?B`0+nJ%7GlNW^K^h} z6(gTQ28?%+CNf8lyxp&WOLI8*=%s& zhG{uGm#nd)vt$oy=DHg>k3k)45Hckxp1`zqwwIt`1EEF-V(scA!8YgA@)k5vk}4=y z5)%>E$&DcoNn+sK^#2b_0|J4@51bWhJnKsT!?k94j66q+}2RrIedC9{Q?Ryym69bN+^nhX8=uT?GXC*>yWw zFA)~ggZ1nE2|;bktj!K(;OF&fju?zX5Ci}%EiG16wP(++{z@MJ zYAJW`-cwc8{GR!$mLLR}yhgvjvU}IArKP26pgtjl1>JVfuo~JPPCW6%S6=X{O4eWh z`q!WG7k>4^vMdP!5Y@M6fOzxGw_N%2E2k%? zA9U8)V76ygOKc>V4UB;RP}M*7*kevU^^`$>rPt}5_n60&-OkENe>fb@&P+e>tOu&| z)K5I=;q?AGk0y#M~`Q#@*`Fq%Hxi|i>{kIVarj`^cgpr9!wo?Ki^1qY{{TK`z zWZ?<*R@rz$p?2e08y{P4N^}Q8s6C>RYM8j`NlLCi*A$qlWNIlls^ox8rvk$hrkbEd z7L##r!dVY!jleV2Kz{VYyNKwf_W~#0kvc-_A=#&}-J>B?@F2)gMc8fIgo2 z(%?|>u-t}N?IWUOX+C);*SqUF)4Srj)#66&{K*)ZxU8noSQp1KNrF-CU`jYF#8Ek&n1_?;o0g2xj7D7YOPO4SAotM@ zndS;P)0A_6?`=^yuQoZ=@aSHuodyINdQU2=7!6dxH*o9+^$IcikNL9lM*UbZ$$P&SThmQtGg(owKLI-I3zLG&S z`40fQ*^KXSGpCso&2G_7#L(pwAOf=cd#SL^tZ^g&$=5r2Y!wtH!8Qi)UZ6RL9eoyN zbs|^*I#W%@=L*>BK4%7>7|H>B%rK^^_Ig9f6x2h1L{O1Nkysh;D(I_fbyA? zYPWB89CT#WL2q}9URdJqjAAc{j8&2eU(83}Ej?m2MkbEQdqb)=gF^(8^7!MAf69}e zyyfu2HXO2nNR~x$#u;Zk@d-~jV#^VS9d@X!>rPn;A&BI<_3NJZ+~+MVF5mxw4^VIp z-*WiVpZ4^_58ra+QAYxRIzeoDdiu@3_ohy-`~8bAncp|xExQkU$ir0qqmMY^+0T9U zr$6<7zwwQ4yykVUnVp@HQX-KkishA+ul(m%4jede_E`^l%;V0hsv)9qtCY-W5D-y< z5K_u@>(;*ZHLpH&!^TriIq5Nvd2}shuh#=9Cnl$!`uztg-r=CVcr=NcM z^Pm6x6OKE6&CE0as11M+V0wD`+0TCV-FI$1?exMhishmm#c$%vdspV!D5Bje!5;oM|+bOY?d? z2Q1BT0q|boIw1tBS%JTp^E?XRgbg|+vYB8$x`fg6Un|EMoKQ$TRTyb$3ny-343c@u z-3^Ze(Qm@_Z*IKF7wN#eK`)F?hkqE%s8RK7C5~p=XyJyI9JDT}&X|Uuz42GF1Kr@PSVm?@pmQ9M|;YeLY6(|F|?5 zC-hdAC#RSN^H~OHDowpd7zd7>Ln4_v%^}2`+L8R?GiX|{S)X>YvhecVj^H-iM;1ed zD1Vgn0CGT_i=QW8(vl;3E3N|_>8TGJ0-5vTtfdTsG7m!_(wp6ol++t5ul~;7Prv;) z2X@voYddu<5d;FN_lux>P}Hgm0SSsuS?t`t|M=6#U-{nSHXb`hL<_sBFZ}hbmw#h^ zX1a$Rs4GDd)ImcaO*n!z6jXLQ`P$3{fsgH+Xqtd}90n`A&4x4_!;WLkQviLtpM4|)LGXDC?{$MaX@$8AB zBngC~(7u-d@0?~IoJPz|UBm%?RLP+jXI+ws?y9P)hjrQQAnJ{Tt6_yj(J4#G4#f}x z3n2gzApHgg0Hl;s%Cam0;Lbbm*tT=~)Wp=WM<3hmbybB+FTM1c&wS>}%F6G)=}qr= z``i2deo+)Un{Gsa_3IAVvgI(f=Z{ZR(gCw~ zpX%DCib<&*>p{DSC^S{g{$P9Y1gvF=$D)!@qGZ# zPmiKLbj#ORwJ5CaQhm-F)UplctFF_HHOyquKNgU)$y zyOBYL5fbz5M?&G#=qHE3`igT+O@}u4xi&Y83Tsb^CT1k1NV6ptA_j+|8bMrYux&`Y zWTURQ<4nw%%nP(n>$I$6?bS4$Wox{M@agSKICjr{e00961Nkl_MGC`Xg#QRhUXeP*Ezdo+}0 zy0xF6ky6~+MtF}7c63=o;7gq6`n2hk_udX?dr~I|fp=b8-*Lmj8IPEr+9bTtp6%vu zyeQ|5MP}W+95Geg-a3KQCiRQ2!&7Jb1^;@*bnQQEFkoKW1PZ~UpzFxzV?DOv?=Tx0 zOpkB-*+wmr@pS-4w+auQGv2sM04XW=g@Q3V;&3RBI&E^(vAsQ8_t(RcP`U1! zbV#5#QU(xaAq^HFkf0z*K#~AS){;;b9S{YgKq(LuS4j2srqT!qK!mWgG&t_KHJgta zhk|GTSP)4-)bBO3iimG>qZ!g@8jsrA$L6$h>P0HYK?T$@BWE0*)jGc3&_C9+FBV91tLir4u`dr2!MzRlu1Z|l# zV)E>>&Q`tEbxlN7RS|(Y%3Kiv9Y09bFQPdP+aMb{#ait2tf6HS&618&wT`kQW|q*4 zB)E!;O`nnU#>ib`>f&sZC`cTj#i0b;WLM?d-C8VGKwh7|yAT|Y#BgQqj!{h(Kckyf z4HK)A*|5lw0*@_<85jZyWVKb&=Bn*(iB&`}FnUMiW9NopOk{;B^=%_jBj=Xvw4Wm+wk`NBO`Wd#F@24x4)g%h$Bea9I#pG@ggoSAcUX`D{4XIB-#uO{V$?cDY4TN70<^BkHGsY4T>t<;>3dYIB-6g<#wbsz99aV5Z?l>)2!LmL4(?kP z4+Y!zh_bY5nb`?2@Lggf!-mqMLFCM;eIDHz3DIvs(bI(NNeODOH)2ryNf zg*iE*)ekfBIQdUsmXcmi3_;(|L|hIwgB*q#_T@NYflya+c5~+ek68EXD;E|PX=17* zk|qsNw8C+f}pToq6#KEuxAaMX^`x>xpba3hEE*=b`ggSx*D89 zqfDK1ghU{vTJi&;03qo5AMO9qSLRW|03;Sz4U4rKyHEI?xh*G+seN{-RwqOe7NnvK zlmfuu;+<=s5)}R%fppjd1bTNJ$OY8)i#VHf7!%wgL zdD$&K^~q0M^v!P`bM&!qee3TZdg!Lb#f7r$2D;XW6XOa*Ad-@VI&0Z#ISlto2x4%H zGnsFpRHfOJfs@@Nh9SPmlgoB4nlwh9)JvqYmy zuzoRe)pSaLOPtm~5N5tDHW~fztOTSioc-zi6-az_$zb@Njlw@!GizQNiR5V~GAqH- zfJ`zeGjgl(6W}c~B%`uG$0prU0Pqqt!K~`a)*KMmASwq0;PQ#KGcsWPaf~QD1K^K@ z4@Gs!ls5&{^tEqf*?=4<<+y%i!=C4p}TE zro5}w2IdKTAv5*Hq>t`km3Sb#z<{KXnt~(^b6JeIq;e|(!WIoa*TJJ`7}2rc%D#`v zb+#o`GMH+pnUsPi>03SP99(`PRtw3lgIQh`dTj+iPR2y2w(m4ocoIV``8VElL-^p1 zz)RM%RzBmm@q6T|x8k-;E#dW1a`z%>Fk6M)iO|$fQUDrm237o? zy?mcbX_5V#Km{iGj@l#N_a2c{gE9@6<` zvyTLWw0^9-(Zv&j9v2big;WI{3 zBZ6khf|@cNY^t0T8x%M(V3WzuUI8n{me1plyrQ=rej%l3oj$d&{0xeR0>2BkcRxQ6 zW`)rG>x_Ues5ZmogYg+T=*a!_j3D><%@#BeBpMuu)k&xx&m@ z0x6@K$xV8$IFO2BkrLL=fN6g|ckY?t95Qy#=K|fF{0Sj2HKIzd}+Tz*=?R`ak9+;8+>TC)^`mo;nX+TM&) zYyh5>_mY&O2k6zoh#C2Cl9@Yy-mPK-4ksVDj3^D^RdYr%Xz25X+ZX{na#;xqI^{g< zbsEc2jFO6sIDC#y+$7G_#6!#FeR8c1;R2eVC975R=FW}93OC%(HA+mcZu37E?7 zE~!#1c;|BP${a9JD=tkAHqI~j{wBZe8J*l5qx0H*rBp*K6c}rYs1}Ya0)O9?oYa{CP$byM{n>-+l0nKnf*h+bwG~!# zkz2A?c3&pR?HfakFz{??Yye_-0fc}j0z4ahi*zqG!I)$VUmgRe*?aqlMG4h_HXK); z`}D)^y7lJ%pg%P+R?AAufwUPHAVDAj5M?dL#(TSW4Zik|cb7OA^!wvule_L2^m<*= z`>pETB87lj0tk>2%A)KK214SapL)n~XO0akDFk2vLZOm5(IPg4-mh4xFo)STP&+1T zZbpuh1H-#7Xs?-f9*ZRskUPj-E$HxtD5`KEwi{e%R0ucsKbYKW1htM4t z060EAzGiC8%w!J==yYg#d3k!naI9MhfuajiLfOTIopQKLbDN9)peCqA0burMVh4SV z5_9b?n9H1w^5|$YYEczGhnKK1eg%C^v9aLl(rt~TZmQ$J?J|zo%BDam=~wX~7NW5G zA60ezz%w53^$RcT^?CxKuH`FV`RZ4`^3?#ayt3RWJ4Wt>-K7fHoCXeVU{FGj2NQKt zI4dnl)|N3B&$_~%qZ68WMC)&o--Yv01K8yLiM5i$K3n+ACTeP!8HJ~Gzw4eDM30df z&Q9T5?~0)Sz@qT16mTVv?BB?IARAz693#|!vs|T7qX-Y;L&d0nKZ6y{V9r8?L1YLa zZSUQ%vhUG!cxHhM2g`HOh^H|qoeuMvAa?OM&MyBL7##MhGwLJu7ox%vPwB4xa1PQC z)4I`BS8d!%C!?s6OT_%{<9kULI;W!=3Xe$nHLDP%qOxeV>M{0OA?3QKMhD=zks}>% zV#36$wi-^V@?D5&pD2 zR=K=YvT&8?@m!{hT*sr*yVmhbCys*t+o#W3>d(f4>NPrP>(VHsqtWtymCY{=GE{St z661myj||+l$!KBl9&*{`B(CAHvdDzp|mQ!-L0(#34 zRJfBtHtDhwfZJupO`f4P*|>x(DIDblLQNFcc*d7iM@%r-hn%+uY6$Nop;FRzH#>WM|4&Ol|4uWNw`diC1~Wf zlA;U%kCD)=f^wD2I2kksDXpe6v|@dG(uW8cNGSkTm8X?}FWU?iWaqQn3=ttoNu9Eg zbtM52U}a^6qy)1R$h<7jZJaWPu=ku|TR(3 ziy&-;7Rv_`9x>Oiye6a)jZqiGMK0ye45EYH0&7~7+>V`o6l%uL(UkP z)F?uu1&wpvzN|h}56j39GU26Url}1MZJ;GHYN{$&p=EHK7$>zo@J6lFFC?aHL|IKb z3=?sW!O}6R6SWq_Xt?0DgVk`Q+1fGH)>CjCX%8m7b1~?_Eg~^7{B&e3|Cpoj$+$HCBg|e(*Vm&?O6^C!V^}wxH@1L7lgD50OB#8h( z0+dD&s1C=b0?MMCo#_eyRA22#DX9htq&{+#D2*Q}1a^92*S0-J95wc|S8h7w=wfB5 z7NvGOtrOl9B_yH>pV|nFZOl7c1e*jg?vvYq!{s0%+IT%hXt6i`6e>8=187H^5D`O; z5BFa7mHm5e>#d!e zl=T1!s0I{eLFHv%UA*=ux1RIl*;601=9-I_zy8_&&KkGf*nj;;)}8#AvE`+@EH(Ss z^-}?*)htC?)&7It&D4y#t?w<_Y^v0&mM{5uBVn!C71#Y};4rV+*0r4CO_Nj0f;nRd z2v`V|QUZby2&4}V2hTJp?$oWEvO8Ek)XEd}jFi_pdBO8P-08@#Y_N^FfhT7~jr5`W z4W6Sg+APGUVn^8!qa9*)Q~f!om>AXB$<}c+NJjA{JQklTbV4cd2;%dvRJVb`!z?47Hu3*+=x*oxwauH0XlmP5dK}axK49b77n$42_ zUlvkcQWO7rj}Yw}0YDV~oZs8hQVJ|3?cWC#cR2IK!5%EMmfeND-%FtP9@X~g zTpGKDjOC&*ig-FlRAflCD#^@ih)3GF4j+cEFIqIw6@352-c0BG7JUv z*#Ry-rlzM`rK32Rr}~{?>^{4A-wS;J0FaQ>CoXm|qN%LHSYlX5)8hq4d@v)79}(2b zb%UY=MBI7v;2-~D>*YVIj@`8J#-A_U_L{q8S?}8}XD6rEuPbHMmrx@Kfu$q@gqg{y zr9Fdhe{$(NpIVSm&dg43>=rlOu<)rr-~P744_kM?a)j}v8fUeRSnnJ8W;6=HaMeSJ%Uqo z2h?zwL>j~`1zl6p1B<&z*d~ui%xo}Jy{SzfJDwgC8-;Zg%yDk%6pTa6d*pD8$fvYZ zieheNnbwIIle-bVosE;8eE;Nj-?_LBykYtjBgP*QN4vR6*m55f-%QK_*Q6mI2SZyn z^(@@sU3-FV5;2(ZZ2b0Aj{LHNwP8zBJw-_2fm)eIn_0%2^a{; z;^@aSR52WmdEA_ctBq{sJSI8xQdP}(GfblD0CNZ8NuktzWKTUq6~v?MW1{BLCdi84 z6Y!suSzf_5;q>M-7M9p#*c+LkB#mB_#mhNR(0;C)!@y(*$t**%c{0e&bLixtbt??as{jUHSGePSf zk_EjEQjf8rlMDmp;CKxg<3ex&Mnf^*&N7?_mUI6q(ux5i#8p}Waw;1%yX0^{?h-~_ zrSDU;07n@=qC}G*TKoUFq;gk@K~l64OA?p`kt_&#AVi4f1X&GAEgvWZ76l+eDWJbp zJ>a~V7ry1V^@n%%?LUB0mSsUmgf#&Hs&kHNwL1+EApvS;lO%y8NRU#>8f1;N#99=9 zDE97Lnwr2D{lQ6RJ#DrISqM-^p^H!y7%BLek-v}C(Fq0hBhe(VsRrM}B5=3Sk<>U1 zK0--S=h;fVrM8-cY_dc9^hVr9Pjy&GpLhjTO(^j0o0i`DyEp&$w^r88P1pUZ+v)WC z1+0{t4jJ!_*Zsj_1$}}Fu_hP_sFA8#){|r1b?c@lXD6rUx@B+J$CbmjOx$u!^@%^f zd;YEpg^)ua0;y4D=R9MAHZdt&&}}He@OKxnE1DiL@0h0m(wzsb)39Tc7CV{_Js#@B zkCX6?8Z>{aKZq`_i`rX}*!WMlxP}4vXv}1V~V9iL9rcr?iW|-re(J0rYB^k zBUh`vFsRM%Otwh>(P26wJL8YkFJub>{Vowgo090v{7t}Zbm%rUJW^NW*w|^QjI|qy zQo_A!W9JB9#M?EmcMNCWqN`_<{h{bj`;ZU8qXe1dqaFi0obtm9dSq1ALr1Q|$SbM= z#;HDX&<@6zKtwz3#(PXeBN!DAvk;|dqy{@9Y8GpR2ydr}OodgII4zR<$#42DZY$mh z<~<#8t{(ruO>GY$RoRMw=bZT0Zm`m9wBI6~G+Kgu_Mjw&Nfu%`6tpAD^}?COceb0* zYY0FxnK-`09D_ag0{{*EvR*iwwT^~bL2d{)!jfUyR&6acMxt~OQeYeU!=YGPFihIQ zZ1Sw4uP^zdTzDG%I6 z8w&s^4G%JZ+srulbnz@o!qR~QlT#&LeB$q|o5||=xn~I0PcWX-@BZ>9hPJ1a0qAu6Q9HUf@pR^Hjqs{d*+dmJMHZ& z&=^2rL?m{LrN!7x*eCArSq{LBgA6a^#L)2yx(->-L@J%V?XIO;Zd{t~uAM5xphkpx zvI|&Hzh04&1VTYV0!ku*P+}>t7!nMm6jX?!kg^tqkO<>tQB>kb-<*H+)8-!XqM4Px z6s2-T(PJ~a@y#|w{qGvXq+RowANM(Y$byc{Gi>GL^~^Y_LDfD!DC|)?|wz@6@6li zz6T!~N9dQ(tKrf+5?B`$F z^jR~*#^k!s!ZO}fLmsWcrA+$Ud9FyfL*m4=m@7*+7bo7Z>={L><1nU^z5Tr=5`GI1&1aH=v;rMy!o*3QBQ zIAphX_XpT^c5)Jt(_yjky@~En-C$Z*B#vi$q3TF?7126^%9ibc- zH79Um!D&-+nNZ)zW*IxUKO(Qh9vlBFYdBoZ!0jC6cqE}ghI!)qypz!+ZIt(LUP0M{ zmfmKrU+#aH7)y7Tcs*%lq+C63SXVhgbFv@kr>JM$C`?(Vzyn7AOHMo80hQPtVxj^m zCoSOwMY#eU7Aa+sNKW(M9nC@;Cljh#`8UClyO53(;S$9{Bj#H5*R-(%;^E z-H-PzF3E}MveOlaAW;I4K#8DpM2H9ogh*0-nuJ&&N~~Atz=4XOI_2z{=e_mlqt5E} z`?5fQ64W-zt1sjYlZlxhkb3$Fz((7g2@yaLO8pfWYvdD-heiaLau*mYaGvH^qXDGc zN;7GhIg0#JcOeK1ECc|-nU7xomUoKJ|J9D=T`N<)a!|prmNiN$5DTpP)#Bo?9)bW- z2tpLSUTt@=?#&aMHskNm9{47X|Ud*7&C!u~IgC>6R4| z88^*(h@_vlkT0uj(gy?V2|yN2*~L$3fXNt%Mct_; zjo7vHm`&wG^AIIHr~pDr%@9!#S}>utK3xXy5I5j{2+R6u_ab&qvffI(ky1on&lw4D zaF%+c4JGbCaN@^{u^qgyrBdrue)SkyP4_~-NJKgF}engmXxeqPKN^wCdf7k)XqrUAn;)nw5YE( za6+&tMKN+{`C@!-_QoQ~rK<+*3y*dqd^g$q2SO?I5;rzpBl9Rz6y_z_XvufMlW9{v zub2$a0z_LBKEcu3wzz}D+^Nbu014_v8C=^c6gd;*hP}k(@SQ&m8Zoi&U>HUuMvw?O zqKu59hGQ`9d8-?FggK6`vKRPZKtRt|vA{>0bmre!EIkkmE$&2;3E53jGMqCzfFOS! z^b(?x>zyMK4~sja9$H|XY(6kcQ_YG12vBsy%2IXQ1IAzfH>ZE=vv>XQYxnHkIlyJn z>lVGSvMfO$0Tw6-)dtFl1SqKhKsl_dfgG&VNU-+M?jxRdb&vKF0D5fHYpG|c6{K}DKTpzCItvkFQ3NTMHwFM)EgmEQ_rRj ze_CwIIKW z0veM~y6d1A4(B!llNiY)Ql1p!q_@>PR zs~{EGj|+n}b;OhRI}f_Zh|>7M{%YLPQ~)K+YcQ56eaO@8duXR0@6xxmXLJLJM$8!5 z(!zviIp3u5jla=C6D>CRwAV`<`(kxZl~tTZ_1S}-OTU*0)}l+oc7)p?Y-nrcv|uu0cj}5^}DeSN{{?T#_0W4#~_rRVHW59 z`syDMN|9t{c#A|1q+AuL{Nz%(nO|b73-~)}VTuNpV=7UWn}p18^WRT+dCwjR#NkGW zc9G*d4H)wtpne-bbZ0zZg88bkbpS)rDK)Fui*XjQ~UX*of3J z(qDlQtVlo5Xp-bp8A90q;XmT@`!^C2?aa(^chPHxNmj323Va+09y>ec2l{VgA+kUr zUtuKSzgu0yehi7EFIsicZsR#L8EhO4@8559$+w;jcrZO)RT463mG17S7paX1>nl>K8x*1H{CJTVwL z`YorCBd}F$EP&<~Nk|@}uAT6K-OfPyTuvC*`vDFlf_VO;kn*D#hE0DFmvg}5TxCZr z87C1TV_K+)$st`vx*`1lFcoZyt-0eEQ4Q#3`J!xQ0Mh%1FQAs+sjG zN+j$&M&hRDG|@MEGfYaPOrXD614mKuLm;it7gkCNDN#}3kR4IxjBW9OmW)lSpm(Fa z^0eQ1(?wmBhBvD}zy@L&Yq-M(EwrQ1KCaO^HfolXFr1O|l*Op$Nuf!~c5Fe{X@CAH zNOxx1j*Lk0k2Bw$BrWmLG?^~0@N5gc_1KGYtmE0uv946s`aZF0zV~IXfbvf{+28Ud zpNY3{d)v`PP1>KR>9F+p9f$03UL0`@QYOB2=teW~v6}B#*~1Ke zHWWNH$WyaEX`WVV;;g`ao**7Ur}&g-w#lxBHeQ#nA-hFYTBbj7XQ-q(&&)F~1S8!j zyzMHA8h*2VfDtePB0#}pChHc0iAU(y5!n!tG;Z!z13MG(72({a#}l!ywh7Tr+@yf~ z*;4i?Q?O7ef{-O<$jl;=D|v!(XdGd;kDR0h*53bH%Ts}VRY{Kx=QIrXsbP{i5SUzB z4WJ##;fH2cXz@%&8#O_`;-v3EhE3s42>y6(D~lZf4{UF|EV}urc9l!7!{&ajdsv|K zskTxQ0|u2AG>lC>A9A?3MlKOW?C@{KEIOHY#~T5wf?-%#Xo`bZfn zL{9*UFmaCgtNfZ2ws;QiSD@vS?ygJV)J;Wh^8w>`hW z%cH|R_#~C7^SP1~y6l^I;#^(s8}r+|wfTAe3zv55n=gc4OD2?d&^uXtg>({5KL1T_ zuODla7}kVt`}}kxyeP|0D*1zRIKS`)dCfgFidWh@eZ&mj$}I zUY}-VM52hR!M^<+W*W$RqWF}pAy&!JuZTbN;K8{3apWLs(sUV6fX}~4A?DQUGs&#; zGH}~Xz=#5jFQpWSEZ_$l6BgCCcVyW=R8wJ1q!ig95%5uC<=?|x#>`oFQM8anoi~HV ze!^^v2>(NgN7t)Ky7g9?HnC?<m7eDOr}Ifj8e$`&$2O? z`WOp=&tMgifC=$d^Q#fBUb4aC=JCtJ& zpsqacZefzFQ{TSQvy#Q^l&aP`Dv{H~ElufO9sOHDBUTwegV$C=5VF_oFutQvqX_0U zv+mL-+0y6JRsd&vG`;7h@m*j3MrZfilI4aW%E%w2RZq)|ggn8c-($k3wfoFQ6~E(@ zG$k@zjZ^i$=W-kSYv&FQBHDVcoxy_IWQg-NSnvVv**VV0U_&Tsuq;>0ey}XB9Rgj_ zN-AicQ)5C$VGPz%Ux!%R_Qcp_p3ye&@cztKl+CzeoErCYNNll|+M4iC;E_dsQfDHS zgRBIJ$KqM67l%!CAh4Sue&oQPo9=W9jn(+&rQa*MQzY@+wYYC^`uFEwe4K?*Jd6!m zSrME#m~p?D*@VG2v-0vLXJo2%5n-I7*$WA$Bz4l?ZPeF_^17m@cc@4E-JgFge))so z*vNk8cOLnQ^vps+_du#+?6KJ$x-Zq@I;kRxHJs#|3;%sVj~ce+5o$OB$Jt}Meb%-c zwfyYScy~W>UuJ+V>^NX+U)g3h@^^FKu73^=F!Vl;D3Lk4^BCm!>PBE)fH_AaScJ0q z>@qu0O8=r664_BJO`Q)`y;yQkGXF1%1j~BMElus`40XEUnmf_R8f}c&v?X!}v8+k_d;`rck^-zYM zwa+7RkLeiEa_I#(o6RceX&eaZM%oEtYK|6kp}(djVPJA>$(t_AdO8QC^LtmQ>Q$nJ z6A)ljdUJXz8%4v};mGEcD1;UdZ9PlPX$#mb-h3%`+nQ_MSFSFm^1lv z@5x^se;*X4UhYfb3pZE4; z=eW^)bJM@{YpJU@^_v*DLX&$ps{~*l+*YtfM|K&k5+^Mz74N@z?4acLw0GB_Nm&+W zBE7qj`u+2!l~Z;$TN~TE-O8A1Eg16-f4>yO05{-@Er&Rc$ml_4QU3fR78jv{AA6pd z1B00)PnxWZd@vQO=7bF82YjtjBhl`sq1&P_%}yuO(zZ;$g>C*W8x4W}rT6zRQ%)8+ zdk_rdt9#ZiaC3|rb1q)wVHeR5n^o8=UeSWgF%PHGSa;53D9On6n_;!8&TSwDwqt#W zUps7#t=$V_m=hKbLv)@$G^H&LgwwEaCN^!-+A@D>qE(EZ7>fmmqf<0;=$DA5c9s`A?d-V zFybPkf%wlp9+JKRnwgtE%zu$waVFD++{1=sLxV;Av%dedtBx&o!ub@E3^S-^*kT!^ znF9M`#NEwJEE@~q>Z5Z=2rWnwX^M^)D4fHGFX#OeqZ6Jd9%}K+;th^BM?|6H%H(bB zRVG|USvfu)C;n&`ECkQ{ue8+pWTmCK*>>PISW<+ehBPENXQGgg28LgH(I~c;8j_0GJBm16WKa*TUv>jVU+w(gz zn_lDg|2PVlFM5vQ4qJb=~#5&y6r zMRTJ}VLL3vWh5EtqoQmqX`Hm5J=BV-`o8IWVd^l!Vt zetqr!Ae!5>4A0u}Uq5IXr(*Q+_d`X;I?l*;;>F=6)eXVCb1GPo#l|}({G`_|LPQ>j z*kAph2Qx=Ab|zxrrof~`Xp}u;b9T&QgwsJO1R8_UmiC-A9eW{D7-YOsw~Vb`<_O&W zl`y0pP-%)3wSzqt3{xe2xY6lmoeU6>Pzi03R+?6K z%C&w}PF(RQ&lxjAT&!5yY{8lcOvHa=NFQQK33T^mMB=_SOV00jAsE@!4AI+e6w=Ed z{4GD2Zj?gu@0#w}f8y!-Yv&PTN8bPOB|WE5k%xgIwWJNi{fr0_q7wR+7c}^2P$Wp< z;$8bl{n?X*BAIEdL_1QH7W2?znp^Bo1-;ACK+~6dIWy)*d^DqiUi`k;cnkR>7PL4=rH4uA7CixB+MNbDytpRi8#a9cg$u(Y z!$#1e@@!D*g!=gWlFR+;4SDNh0zam`-9`=wLNA$I*RJO)v?QQKU;jE|FLru8X92w+ zfd8Nh0HpgRL0)!6>mByTA#Vo~K&=TElPeSz5D^UEiTR$Sp{C6mx)fI)SG~=12|RS( zXF1&Y0~}P$2!#k3pr+WOct)DZL@xjf0GYl`MT2i5ZJ_t6YC7By(}|m#9Xgtfmj@cY z%N2(S{*&t7r`JyLVv^^I)gScrQPQBAnK+pJC^99me#4#YlsbX-)m|Msfuy-8^eCqVZQ_T1Fsr!F@W6w#F1vmWK z!xgGpr%gM2w&6Q1y85gSHjZYLp+1SbcqKI}Y*p({i^lw=Zr6~$Sm?bX7UK+m{h6)n zKx$NZG{lX=NiuQ92nl2yApYT+(=+i#&eBFIu$L8!f5C>0&D0ySTbKRj^h7(3hPh( zT(N&0GH)#t-%B?C1b8ZdZE08w?C{)ZwG0xHc3U(~dL3(9=Py5M1HgRgh=Q)xr|C%= z)z$2x!lHD$j9lxEs}1u}P6JYNCQpO1D5S(li5Vpd3SZHSyINI69@=*@PnnjT`D>U-<;JUZ9`krfdYn^fo_$}OK@?I_ z!ahxDIO;0?31QGQzB7uL*Ii@ygWJ{+&4pP{B=P{Prk6dgtcM30!Pi4DpjqJpIr0+Y zx}G-7>v~?^L+{Q7u3q-}&*f~qu7_wc;X#zMQH%!y^8e7}vOl!P;Frf%cCFA7#S#|; z;bsOP#QMQPvfZX78CE0Dz@2i;EZ1Aw)ELszE$!{vxMGTli9R6@-I(Bu_0Ah(*rjH> z>*aN*DWImUwD>Z_Q--}@v0UXWpKf7x^UZ#ZAxv0W3ItH+*Y3GU5AG4cw`UOV&2Zi9 zOzV#QOs@9LNWq6qOllhHD88#=Am9sxL$%iY6NdAYPkx0$r2frzF&b>Yuj8_EmrrA% z`92Gg&C|B(f#V~O(LsG)Dl9qBs|UHwr&+%!(|f(;eS128gPu?D->*z72u9Y~D&m^^ zapNp?x;D8}d+s-|Ff(Vd6v^kYfrL+oXZY^z8?xdYE36y@+lWhUW}Ke?^$aipF80w*WRcYhZlAct(BFQ6Wv#3usBvS^H_3_~kD@B!*{b>LRPR|~ zZ{m=dgMg>$i;H0gY1zwm`vwovtnQ)P;IvkWZp^_e6K&ql+gfqzgiuRx^roAjorugQ zUwF#%N6P4CB>F90guD=W`DqjNsWjsz?*-}8(>RWr1cKbS&RB(*gi|qT(xk>@0*C|u z*o`c;Q0t$NRwYs59`_O0^?SS@DT7>He|*v3o^9hzCh&9-Cfm@WX2ZoZ(cp)*cJ~us z`you3cEBloZ!ArwsSR50;ieUtQ!xw{$h*=;nEU!k@gG$2hVAFC%KVx1*7L6qV+oE` zxj#0%0-|xsOV1qwwhYQU*{K{gaV?Z6;;42s&R;|N(QnLcn`9AhKaeDjkCC4puv8TD z3uP?Y-E*rO&bO)=rw{6zlExJw7}|n19Q}m9I5YU-j?=>hhGyuAhi9+iB;WpB$`!*K zMZvMMjuciXxh>X9vQ$o>hRqH=JX>kCKPMK^fCC}>(gr?-6w!vQH49aczL!lLlVdZ` zQC1Y<4iYK-8N?w(2m0XyPZUFhQBBiowEAy~_3w}oHl}gIl5jRw>4zL0%%DC0@IqJe zj@HOQixHhPlcp|pN3#+u+N~9;d9i?SuNk@#Js^r05V#yF2`#zSgVx3?@F+@SQ*0*2 zCP*_%#-PBJ@LRz9oT(R0AC~Jh-9=1KeA_$71LnGHvKUp>5zRZbpHX~W35wDNV2QC1VF+IMVtQ?Dlcr^6@<0RPfu5niC5JGw`#9j!@5-- zPi93K&QFIDL@i?33!PpA9FX?CAVTnM3HW6u`s?fEy4QmZ(5@>1r2pp|u>1xqX5->y zuVxjBi@|8iG32P4I&x^svRxw790!<_W&RuNC920!)jO@W;)y?CV=0B&cRZ3jpCL@G z>vjVG#cCoeGqbVW{a@%k@7CbwqgI}~<~&6tPP})mL%!}uRE|p-8Z;o45}|*`hcg+q z%da_@@dwFbjDxZkl$xJa6AYR&eb(8C~JtPp=3r z`K%8cr+Z)gF>{u3fTm4MF~UhE=TR@4g`3k{H9iNxm9?9bl6)ttXQuyIahw`5qboDv zzE}74Whqb)m@K_-wn@|7kA~FsSF1RJasCc}@IR*g&_}HRmls~R73}uCvYS5?#y(?` z=%wjC*52C^Eq_YjyANY(yW7oRD??52I(FpASayCtUUrSS-psdaGtZ@m+%dbqwBHQI zW)sPz1rOq`PzX2&F6jL>uLnym6O(r@H#jK2Da|-j?``Ck@9p@w2wq{I^D9R(#MYwJTFzwysDj zuJAFU!}KL2tthzZWR`lL$2hmqX4K(VS#nD^9u@W)deixuRV#WVAveLUp~ftDvAmqy zJbmW94&;HZLnzj!Xc2i_BuO+e(na4siRZ4sQ-{a16-B9QjLn)|XLw=QyEF zBTBL)Y(*LDtOq88aWO<74np5ppOtJH`iyAl_WXI z$A=1~%?Aky$($oC4unj}a9Z~B9h}BIh%AQ|DVCl>1G8j(z#NGnni&_=M>+Kc^*RY8 zH2JLP!A(A+!N^QUBY&w~e7q@oADh5oXpjRGvPy>}-}tJG(pgCT2!lm|2>`ssTF!cpVQK=; zEfexl>HwECWHsk~k)Wu?a4U3Cq3WS%!t==&)3a+j^Ge0C{!eIVz5 z05_zS{$7y@g-!FF9LCH$G)}LsMJc>jui|A2;uGtS=?sa;Cm?=zSQwulGCu=sHK%c9 zOuziNTWD@gq?dX+obcWY2M|6+0N8Kcd#(;kQJOvgaok8iJCzHpG&2-C_mEt-Oa2FhG5 z>lU|fM3!syj|y)$R)D>4#ZNV&?}0Jb+Lh8!EG_z69i9pU= zjU|17YxD5x&J1>rZzUp6?mC+(j8$Ts)^@&oDtI2DOvVsaSd!|@F9?I4M)O~<4N0n% zbzU9qzrAGFinuPqmx;XvgJ0!@y!%qj<6>bLs2mHd6XfZ5dTs26*g{ATHkqlhVt%lm}7`IL_b z)Q^1fef`3*Lo8JKjhm;cX4v{4zM?+r-t-mq&54QTNi`eRd&VuMZ(>z?2$=LeZLjQfu5m(W*yT7)!3DOMLPPW%+)@%Jd(IpTPle#7LA zrB7H>KM%?JRSthf1aRIM4G!=P{mo2X+raV?9_hu!iKqaNH z*bk8++`h80)X2GH>64kLBwXwKg9RQKaZ* z2@qeSx;=Z8(1RJC{s<#AkeYK4{dCevprVkMl}j|{W@F8IW|F-0Zs9~giUnoC5=WeE4nVgH0tAb^2CGe-}P4SMn~t;`ovyj!so?A@GX zK%T;f!3`4!fmBspcy^6Q??0i-&XyQ68k37Io@#zmRh_^ss3=X>2)Y;Y;0`JXKYj zlcHI-Vg8h0EVZ{_CNIu=KFoJP6fE34azXTS)|R=L3NN=B0|u1CygofEoY3=8m5kz6 z?uSMqsB(H!zgP@a5*H{68R+LV)&QoV(eaAYh-fuYC)Jz;R8&+jFsa#6p4VsvHS%!?o!tDUacTU=LN*^~XCfeTZVMrslPS zsubO0O!u=2I!q4jzb(yqC##>$>gSmuVZyj#_@EgLW@TJ1XC7ktR;d1u`9A<`r^CAF zB%woBBXM_Sg`ACzE#x=1%jwDq;PACy{-O1bk__oxrF#;kQzD8GQqGZz6T0XNds%Dc z?qt4Gskx`+N$$DVeGp-#;7&|rwC7EphfxP09kwnC5Bc)tb$55yC`!P@+615}QBuu^ zs3#)$Z2U%h2e;iE!%}44cw(Xv(ql!rGnN`Pg$z$V1DZT^Y*wPA_`~oOMR8t9`muR{ z|BNl`arC~^W5_s3$9ctWAknzw*N2Eu%-rb!m~DjXb9KVyN9MS;Wg+Gm&g{6Z<@>Wf z{l$)t+|*WlRcV(imtA7xiu8Z)$lWntD8w>Zj;ylXYZmX9ti@001 z+oI5!hyJ$yinR*$S=Tw>6AE&=?&Na8Fd|(vEVEf6OO>e4pC}$@oUGnN+j$J5`1ign zF3JeI>1Jg9*T+!m2(EwA?vt$6GRzY9BxDaKRwLhqtu39H0#{<@rR`YVYdvRwl9ptB zLKh5M+?qbqc;hQnQA8s&9n^JAsj>%$G=WI@jVHB|Jgj!{;0nqR5G5(IoG~h6MsidPvl=AQ0EO zDJFjUZbNT%7obR74Y%=0q;HtwD}0x_csCAh1P_N68yqYWDAb)voff6oq6K=fhND*v zjhaqWy9-VG$`ATt(Yipc@p;pg#qN4{1k(P{1?O#nj#PTPkKjWePeld7&22v1zgBy* zomkjWSDlZ`CsR}gyBQly*SPN4$XW52Tdq#&fUpWQxI{rG)0}v$OZU7_)c6_Unu&bF z*M~I@*pjB1Mja6q=iCS>(hahbRueUVN4}s>43>Vx-rd~&bgALUs-+ZCTU%S*OcwBo zG!1|)h-ckDzXED&ICzV~d0AQcat5Yz4b8O{BQ>4*k&$pfSh8LH8H0w-)YP+aXRGN0)Y&>rR-bOg$bT^+zCt@+pF!1?N8e|&1gmZA=fBeJ-rU?QEPPT0CR41f zuAZ$mevc=AJRu^|EOm(Y>%zjq@NigzgE^s{?>82KpQ^WW9%qMD?O0oQNKU9daSZC+ zTv}|2_T2dla84W0OI26e|DB&NHCPzC;lHOTqMipPI_EadKK){u40%&^RTrz(*7{~< zBDA)Svy;&iKsZ`vFm{RSzD`IUAml5isN&RNbG`t*xA|&=Z_Hwpy?8T3_?>VdiG3vD+>ro31hSCV`o& z(!&IK+?_0f^&wb>mgP;}uR-!tB|#$9HgyF6Oh6Q%vaXJd$i)4&_cUB>b+XP$xzq{( zip|az+gzR-Ps3$Uh3dU~pX7oqiH;+0pS>uS8oi#cq`_|0N&vKWO{ZzqdwJzVyJ|iy zH62&@2C##4c#P!!Kr7MzKwFKas(QXw=K*}{AYkd--`kr}G<1I#SRB;5>fwyqwYbQw z7-F^DG%+!uHfswwM6L_RGOSI%5taV^0HY101OYqr{itcW%MFV}+0Nk03yEF!LXA^e zg1vFIJ>Yq-(&6Re;AY|EtW-w=IRo$Hx*-JqM%%V~^52iGrM2>?_1$``qn7Ju++4t6 zz4rJB)dvXyaOvZ@3P6lIT1%~wC>&xq@$$0P;Q?@Mc~;MY0FY*do*u54j%tXQ1`6FS zGEKVeQc01n#~4S`iv9I)wjj5FVp_XZ{KSXm+qv>8fkP~J32%!;>x-VaF0m;d0~HkN zMH~O7ASi3DcD#1<6mRvUOjG07FLAyYzKg~VwuDsOm8)v1PzQE%{a8PdiPkYXlSy

$^j{i0w&PEp`zDi41JfvKe_~&Y`isJ$n<;S{cs{S-9ebf1?6X%KS&n z*`Cjx2H{+QN+&gc%6F|UsPC=wRtiP876p)eTqQXd0%J1D{mrW9(;Ab#2WOeLS-i|D z*+xGKHM)m&WgLttf7)p=&vs^wdS}pSBb?KuJeySs()Shpfh)-&SJ_?EdG|w9drk3e zn{?fM5q!dLX`UylID5OJVzWF4q*S&Wd*umucMhyomHA`Hz9Z42VC&$D)!e53vGIP% zBzBoO=DaI)4*`@Jx+@;~ud%ZTV5gT7g>*jx(D%6jlN_WR5*|~(m^lJ)ywGQ?L`vb) zgi%p77{a0AA>^Bc_C&e3e9ke(Fjy>r;(9S=<9U59@KjS)_3~INCN3XMRy>Ff)n^m0~sx(9{C(;n6FvIfsw#^_y?>9`a9>$?lmB*@b z=2Zfd_u)EpR}X47vBnRWkRP2 zUz)0?tgNqJ@42ki-;%G#Jr*CC1Of2-z`%e-y!(%VFu1Or9e9dLowi0=ls-)9!qU>8 zdNRo5?_M|WA#eWRhaH;A)~o&JyYu77$c)$QI=$zmc>(BUG~@79u8sGxKd`&bhRhr~ z+B}X`*m!QA%gIK#wD!sTr();3ZxK_Jj03WLOA6q-tWKxR!26XtCr`tZ`e_BPsc_w^ zh$2BFK$N1c`gBZ44$9`5TkY_e=zD|y0RzVe)^B4~z%VEHu6_5j-ogOybLjT=w)HJc z?jtKsm@oqa1903z+PY<`)(*I5pE8nOq7s}m5|^87-mU;@$}W@FU9%DR zxhd;$WnbWaFfZ!J$%AwI9nyaaur0u-vM?Y}<8+*1F<0)^g?|s;Ez<=eK!HkzS%Si? z1*0gsk^hj^;MX(AQ*boc^FIm1F^o@wJu{JtKMEF4MwHGmJg8~S`*|O@GmwkN3BZ#( zCZ@=DF^b4{jSqSC``E5OlE}ze1SbltLIdL&AHA>B-fk8rbRBM0pYKT*>dm&}GcN*A zbstOr6Jm-fQozQ-!z*S>$2b8f(2>4^f{C}|k6&M^P|FP78min@m9y?tL;(kz4q8MoiBIm`||Y@A*u3;Uq~Qrj;OD&mpnHS8pdL^ zx0#!UL2iGW)pFz8%S4se>G9j+bQZLJ{r{Q73D+kZ!Q;gVfwKi8MSYI)z^;rom)EoN z4!84{Gp_c_$TTi%&*!$a$KLa|Y^*+(0g~6SB)yA&Nu8D^CXt3CyyxxLfOuC9bTww< zZBaG2H4 zmPTQzy-~S$yQ^D#t;MuvDsC-DtcONkb?s%H5;(s${(qdy{q{2fOpP10| z7;6As!~_dC0*m$}DNr8(z6B1Ju7I@6u-*&N`>GD;(|=ZVF}lnrEd}opt#wuojt&6) zI&=?yC{ge_DoWC8UD4Bk7`(P%di)Is9!Wr?k|U<^)2YC|LHe(#V)2TV@9vpS+uLop z9`tA)x*fG_BjN)bIXwP%O5nE7?`~}^UqO44uv;A``1*+V-v{b^>j-x_U*P5xfDSs4 zLid`W-9UwaeARpzJsmuthPJUh@ii5=Sbuw6w`n`QD}W1E#);?D9O-nO4ok<5{;8q# zhy=zMe#qZiL2j%1O zwFrYb>Z7I%Ea!^3-BLLRFYqlLX|3PQeB&K%<*-Z}-Qe9}ZFsc0b*krv>3S*S>WO!j z^Ujr2T9==1NNBKjzvt*3fz&Suk`?5)#vMg#PFG6n%-2?0&{@zO_fshM2~_09SuCu` ztk9oP6!&T&IN4`#=1uv~x9OCqsVfd)@R5m(OtnBuf4O+CtM*}`9!FD_)Kj8Abh9gi zQ@cz)(S26}N12cRQS5!^^Pw!JRhL+mv2=^zSnywjP-&4Jq`_lDE>R(6=eHlSNwJ#{ zp9=S{Lk<;J6%lTt;qQ7FiDkQirAH&HE~rbVwgtKxv`Vd;22|;@abm1-R@v{$qLDnHXWE+a~#Z=dWuyrsR%IF*d=s zyLD@-+`f)k{SuP90um3Z9u3#{jU%TXK zWVTwNms}N3wIR*$3)_=|>d0_ZeVnRln&6+@AJU5I_NuBJL>P$vzV7$l&lxVAH@i%5 zKDst1)nmcLHO3<+Cqy92CD%bD)-wmG4jHqd0C(eP=JL+pm2n6{h_V&zC!Dq`wj0SNJt|g zjf>QMMp43Qp&AA>SDxbi_PhoG2$TZe%Y z{P^i|)9FHkgPogM3xxF(+8yb)i&)6>vvn0BXxgHX%W8?8mlqY(A%CtX@XSr|G#Mud?z(?402j?q$paDvth=X?36e9O5v*)A1jlT4~Pzjnv_s*hNb51XJyHlv`oN)Bz)4 z)p&@T27rYp<0wT30%=Bo9T)4Hn)A;96ocf6xSWaHi+?WvVnc-e6a$NE|Glm>?}CoTrvc3jZY^K+ASyX#a>;E``0apO8Ph|oI5 zjgyme&b;A@&%>mTO@j0qqi?_7Y}#Zjb9Q+d6;x+3#&=xK791?DS#d}D_96jF7O;B4 zQ`0VSw6e9mfONjhnxx*{-+^yFlWMitpURH>-&+Im zPE`aNW^{Bk_=qh_%;!H{&)J$uUP`u*4_!PW^g)oX&^G z2DWR9v5D{P&GFI4`4h;x zccl2z>S{bEK`544Ts}n#ugzJ9%etJX;9Fk`T>R6=aXs5tbS|t|cZ|y*2-GRd>kh_8 zJZ?ON?Z54_^mO7^4g1I4B$?dHpWz|nZ5J!hwa&`3N;TrgBdD)0yA|LJD}zAv3R2#V zW%Td~NbpH*8Z;|-&pSP)Owy3ap*O*T!2jKmgrNrkIPxcFNAmwDrQ*d?Z*K3LoSYzg zgLyxzblKRyIra=FsHi)3`ypsq*A!^xOc#xPd**`RlM)gVl9Jj)3SLb5hbMh~=X36T zkK2%t3Em7eisHuriBjy10-tG(ub`j`yn0OHeXz+lAub^S+zGJr921QKKFFs;xyY*V zcirK15nR`e*|hidv0l@X6&>A3ZS3ZIiq;($NSc7ro^{)q_p+7CUYusdrE}S=brZMt z(ACO-CQPsn!hU`|i|=s>T!>7Y;n3UP12vtkg3sj@U%Scwen|?+gS9F>-iuDaC$;C; z@1*30+e53;SY<{!6rsXo#kZJI{8?z1(fw0YsMTIb3q=$QiOTR&%#rW=)T8ISLv>=8 zFt%h@)Y}a?^LrBg>P>%{%ae@^cISq*aEsmY#Tsua>DhsX6%{hGYxDMr%~^~d3Dwk)*byO-kUo5log zeg{2hRd~=lwLMlB9BN;6alDM0*k913C(}zC^Yb*M{WLn)?VgHUC>thGK)jXt7h(On zU^TytcSz#S_lH(EPBuBo+BSWi%7V}8Y+h%SAC0J*r^_k+kO;O6&Gu-2e`r)n@%X86 z8r8B&Z^xaY=`~*n*Qbx*!6sbITj~4r>Zgrq?b*l$Uz7D2YmfCN9=ErGJ_z&_Ikao+ zT|^@^Cx?8^81%s^y2$?XJ3Tv`wvrHc3;D-pTv$mEzA^s8J4&7JELa_EKd=xwc*R0d z(kVS_V*A>48vcp?KKq9LN{TEAPmNAj=Xt2BjLGeM(!*47W=;v1)w#)KGx&DbAQjw? zInS8j=aW;+bXMjHUnGB~`tvsHizgX9^+!hmw_)5E?qaTBZ1hT-)7MnGD%X)wOG*oB zpC`uBR41cQiu=qTUt$(d1N!byW33`#kEvQ4okXDE$0cjl9#p)qLa=d^II>HpcVYm4 z`L4>F&Gc*q5b?cKNI(i8MfZuz*8JBWA)Y5S5|G=9DjUeEGw*4;QQ0bXiDcX_{i_}E zr36vYJ`0~X_QS(d>-@3K&Q5VQuMw@aZyskSZD;Mzsby6ppAH1KF4U|VEH&1~F}QEA zK10YyA}TjGLHUwbC$ zxV!<|!uD0pW&W$3Jjl20azxVRw?r*#?z7ba66c2;Dfi(qu66Ijf{!*eeIKg0uLgbT zl%HE$F$GWlfG$=_RRv!!6(oWsn%2x0p9QXbMKF1GLI+5nZviW=*X-X27~`mGg^QCExx0{)+3hT#@HF?{uNUb9A&PJgAm$ zgpC6BpF)kX#@g>fv%D0_(1e7By0WU4z4SWp>iRsm%NLQv;bgf`KGS_s4{#sG$z*}; zPNzq*en#)plK-cDo8WIcI>=kMzgb=rr>yGOdjhJ7U4GjhSm?bi!61@+b2>_>xx8HK z?2K1{9-%Vvxy)zWuQ)GPoO`T$n2x8m^xp9j**$Cq6Wjf5T(+EEUA1!DfIgGDKC{@Y zHe2_8(6V$p;f>}!$X45Ndj{meg6Au!XC3FAT#&~xH66Fv@wSun{l~$J3m~X!S=V*k zoH3hTyXfa?KN?h%OJTR&Uvi+{kc z>eT{BV8l^rb)25sc%3R%kq{ELzBclnLC&Fz4*TZ~69UtP@>y&)XY#zK?0kIf&Zh(v zOD4-!?+VdCevz=pB^L`sB z>CA9haqHE4bAPsKvT9m&MkKMfg&leUe#qk)h@jVacaLZHXGCf)LOGB*XPN^4}1c=Btmuwm6g3+oLNS8_BeO8v+fRuHP_&5(8VPdH#cz()qJ17BQc+}{=>2WaS6A(uI z3=tS2T<>c{AgJ4s)P`NDwEew39_sg{_&d9GUlzTjnwCj!<;{5XaW;6d^DQLTaX3}* z-PNe+hVilW|GlYO$XWO)yJ06HI)Hdtjoir#pQF5&3Vn_~If~>9{y7#EGWd0R)XBhV zXDgcXS~hqTSau_hGsqV+a9stgBxioo>VV?RIXPp$U(9oPgXZmbAU)I1vCR(MeD~U2 zU%--9!lD?XdG~IhS2GjsLS$W4uMJmq{0lEl`^(eGX4ILT)!3w%A3VQL6nYPTZ80wI zZK$Vf_h!b$ON5o4(LD|Jk{E>QT$5^y-qEs4jwog`wnefmrF#*o=GuSnHyJ$KdS!|` zLjwuW^=HY6U9%JY`xXC7%kIJ*+~V+It5zwO813sF(il(3so8mThpxxixI0?7s%-rb z?zG2aU7^Mw<%2213lah94wO_`6?F>s_ikJ#E;yilHJD9q=d8t&XY1vQ28q5mq_rB7Q_ znd0iNXUpX77Coyu4ZXl2BIyYEtWNtcjEOsw*GaCl>U4c!@iRtSF9#7#4s|F!E~G77 zEPU}iI10n?h&dUz!RwIQYx5eb12%{J{73N^GZiYB<9ZZk1tceSb=N2lNhAreYdY*U zY9>3IYsf-PWIT9+*T90a;Ewz>%Uz5b#e4Y8DSlOo1}HK)>AfZ8O@L7#?T;0i#;f3_ zz}B(*S#z*fGOO}@8!K%2&!m+P7VHKiOoCD?q6hdgL<&vY;S{J8X#Zm@JmXgJbAudt z^1fH#Qp+(eSDxw9Cl-a#^2yeNpTuS&8B#fk4#8I)0|3E$5_F^wG?5+%lt0pQ(vv)Y zLKsX}aK<^S?oTTcRxi;EWGfN%Ku;IRAG8(`X6?Ow&4vbo*lOx455esufS+dLT6yE@ zj9Z-2!LLrei+z@>9%mp{1Le7MqzSKZ0+Q+CpxFigS(m~9e&V1QA8e5eTnZK<`Kx?; z7O(e-tZb~t6ps$haOfn`j{iXlE)Ni}FqO z^@NIWKZBr*f}m77^_pR@@n&i8@Mwtr=kN<2zhz_d8!%9_o|HcI-YkRj2Q4q;=GxO& z`mT@Y!H5Vj#xpas^4qtMS8ntt&umt-Qb@oNw7y;lnT z--~`roF+y{W$nF3f?_}Xd%!?tKeEPoh&DpGFFTx+J`u>o4vM>RlyaKx)l0Ug+IIJ! zY;58+$6V`Lj<8;;hiz|7MGeVd(Iok3C{!yS%Br-t?Pwu+yoFJvE)t}^hIg$Ww=TBf z$_x4(~wM&Qo78vBfAb}Ln?BpTm4x9Gk4A3 z+s{#!6V9dp|b$2Ccvcdv3{@4s#y*WrGlZVPf>&HC} zb8h9r;S6J@pUpSNBU_8{%wYj@LES0=C>i^WJs=wV87(w4eT#C6^iVw+jxV)nUiiaL znm-FOr`tX5{}ISCf~*VpZ+~!mDR!_EnL8a@i`LT2iSCK7aaK*zLA+H_@C#u)Y2tbMhwgf)>(!7X2^~_MYZ}Ym-C#m(p z&fzn-s}$B~jA)5g`zNsM;}PevbERmAwx4;fDG!*KZ2PKo(XQ7$-)N``Ot>M`J<>77dc!d7{$_CqkG$cdce;<7R?HKR z=mP}aR{v6qZcQ}N36bWdT-w6c;mf}&MlKqQfIf6XT(272n^}UcJ@YU zR~%uPN*Gp}rtOluOgob41=&`T5Wxk0tZeG4jQ4t7=wyQu-B+-dabk=Radn7*NYGGi zc_vjpaG?k+6U;mH1^W3d8-zaU9m&IBEw1+ERZZLGUOpO~OQjZH9)3?!2C+IM_gb!4Jeu-Iyd6UB}h%GIoawXLQtnST8mkF4dpBE4k)otuMP-?-G@y8>& zHqWXS*IUusllgiVOMWFgq$`O0@SaXhHoZYZ^L`DxnPyUM(MmFFRjfh4Ll%DDA1!#O zio4fPjnw zMI!_Xef##8I^=*BUBrm&-H7kszgw=ljEDdaM4m-hmY5LB; zW(X%G<-Dk~_#-TAo(e{T%~%gF1U@?ic9_eL3I!XT=lqYukFgrbV$3v<3fNL67=L_D zSV*|;M%M>LcN3&D}3-AUkEN0(@@>T!27aI-a_iQ zn@OtWaM5*tk5n;8(Jw(|sCl&`0nO?BL|N&!Y;vBiVNC;`hGDB3gEO-VKavCLC)i_4 z*{@JgKG^=Fn;5!4u$gkobOb-C>ol0Y&L`uurKOb{ZQDcZV()X%iZa@P$?VG|tt~u* zfco(Gc>V_|`uZ73%}~k_rlRRYN<%{fA2bxgsz028S3i0~yJ__?xZw?973Qj6jrY2M z;j0k4pPxc-V`F~51@9po9 z)7@{}4`$!#Z6{{MR^KMR&DR%ipZ(XzFiH0d8r5@6p!m=8`_2X(e_?k%cU>-G{k^I4 zO}?N^kL`|2l!j^DPj@{&TkW51LwUHs?@~b%enCApz6TDk4U}or2fXhO(p}`&v`eQI zQj=%7@7%J@>Fl4{EzZoZakFW6w3^y|x7_Aw@$alJELGn<>Q3fS+{wZ|RIx(KaW?^p z7d2MHQ>1*OupJXSdL>52w77f2btW+5Q>Hyxt1V9%-}N*3&kxs@&W{cJ@Oyab^!bez zLuRLI4dS6@?LI@Cmph*F+AN$j375F%ab@aWQ*;y1`RVl7yWpuvaOm4|%X2dR1wrbb zgX2bpkJq5Vrthm3YpYgucq0+HMyXq&9-6uhmP#xIaYNX|JI^`N z3PeHGQ=@AWpbZh9s+EV>q>U?eAkh1XnAjE^j#=vO95r~L9$|fP5ay zrjZk`*JbFvoUBEnSAXh0EkUq#^3;t<;nubwvG~@XoQk~UnQcYO7*UD}-y80x>|y$V zUnSMg&Ds)Z_VZos2?@jdo0De~&Ux~jrX}awe#Smj8F7nZ?6_bm^o<81pqnVGkI1xw zMBI}_Wawr+SSouYK>I)!uuAu$h7%H?9;!O)8bKlzf_i0j_x@?bms&M@4$e+X58Vwrii|tw74c>{E16B4@-9|qptLXP8 zNC?fV=5p=3(#^=8vv=;U>RVx=HRL(0uCSn(C?+GD?}?77H`g;lZ=5^T;-Jr~5M0JM z@wG35&h0>PO-mD9^t`|KaB5mq@6ginVMl(c`8nHI^UX6;R~GZw!#ZuFJC2JuG_v%1 zrHCgAjeW~&k|uMClcJlS=4@~u2K(fK^5_3OF(5A+ z@k!3p##og0b)IIrvou45Eaw-${Aw}&{MuNgr-EPmlV<2$=r=yHk;dKq>HU%MH!^`q z*yq(6!cg{fiV(u7kEKrfN~zJ~vB*E)0xR`Fn3It80YKbVWpct6ce;Zc5g`ch?|8(C z4l)xI$Jo#xQxObOVI824e>!JN3aMYvQA~GyP5~5Z34)1++vPmugSPgh*VO0e+KN_w z$UC-;%x-ZucJA1&vCF0qTL84@mif!D2O*dt(nIp4b2rhhW!B9FCDXva7zNeWygCBa zfA^OV3J3`s@m^aL74I&?*U0_mM%a0Q z{bAV)wCp3YVGHlM3`mQ*n6Du$%heSy6R&^(Lo)>$kEz>V96dgFiZ!sk@4e<15a35V z_y*ZOw94+&%DrU|w2{$*=IxW1w{PE;?L67Jc;7sfsw5?k*P+*WC-BYkx2F<5bQN^% zOF1WNT`*K_Fg625*~C*1F>@qfqa-#F$u>ZLST`+jGOxPwsP+@>sqbvGG@1G-#zMNl z?4x@V9G<#KkJ#>(jPqTd7SG=TogT@<3;Db|i>PGVI=ryJB-MD>VfZzQF(_3qx+U8> z1$yt%rXC<6>`4z3qAYts9GKT^rFUT9Q>ficZIPzB`k7*7CN^UP%HSO&0qhg+&+mWL zKsv%4A0Im!lw@1r?Zrauckhh(B}=m!-_q~{>eZ6!JdrDElhD9g>9-uKmE!j*N8 zNIU>{JfgVAlbnisnimAvMrBhplTMQYrag#Y@PY%uB2C~3!wlt2_DTyqcqgDKNPqnB zc0sI`kg0&HGq!a6bOajBw=dy)Us?Bj0yL=*NWwQOS39{BO6QD9AHRltmQRGT2bEhF zzyAW&%@F!=JrlZ>Dw01?+WQAI@Qpqn}eySQlEI5dS2@CzqNmX zC8r>CbaeNiR|$5wH9&!!zqg$D;lqdTQE-}51uhv-U15EsOGbf~rHPv;3&B@_MF@NHo z`&6aikOa4vS}67HRl%R1e)P*gwl;7s{&FdG58sjKc~+sLdBc;FN%9;c)4mG5r{4Tc z)BntW-}Z6dh5C&A)9T$n>(p(t7Q2@VeEU%yaSE~QGG}c>tERG-!u(eAci6704*b*H z?GsVgV8pMh7g7q^_cx5DCy71zhj~i@Adb1H&87d9R81$Urz6Q_kYlK`0*ZUP-Q!_#desMLcSkZ4!Q!sq$Z^yjszkoVurQX@%2n<8NcY=~7MJN?ySax&Ihy zXD^qsJT}cCvD`Gzu4O*gdMz*b%#Tai5#2@xamUvJE*vDXSU84U{IUAPIAMdid!+l< zBY)dIG3ZC>X?Lq@e)Rg0B!<@z$bMIF3oe>VD56(<4Y$Ae{7Qb4l)$~jw!gOZ$Jd;~ zHddqrrR06XMXT6Gq@k>MuxO%-#4 zdtp@1OnW;oaZ_oBfyoIESrRtMdouIDV{o|VqzzGPOxAcdNSaE&PHUU;VBH)ZWt2WW zGJF2@2krx=Uv-Z@i;D^A24#cRLS?&+ZjN*ikR7_XljT3Vpn_Km$7BU$OZ&a@GQ|Hk zH(03%J(|e$(m&r%uw+aSmH62V3A*u4+=y8nJ*d^)jmSiie3n0%30{dUI6{F=adXom z9X?rZ#k6rboyYjX$I|P4lVvCf1cg$Gh4xhY--vO4kee%pud9%k;uwJrZ*yK9h2JeSx$4fwNOn z;bkB8UWtT3lig=ifoKgKlaWts=u)A`V=wXR5QJH zg@wr0uDxrzI%9FR~DE05ow9m4gC&KUyzeM&5Jk zi*t~$J}7}Bqo}O2cQv`?-F1>2ENRbhh`)<~>RyTc@PJOd(Ic$JHGDO$2OPI0`|YM`l|aM4SD=K*r7MsbwzzNZ{3lXZt^x_!?jHE zJxdZ{W0N>An)Vm$0H8reO}Ka0hMZJ(*q~wvzHV~4hpy~N-;0E5j(d!{yX%EHfPBf4 zzIn6A>)Z5rN?&M7@DDZ!dy=MzPDd9rw}SgDBR!cM25GbYR7__q+eY=T> z=13?F#s!T(XciJ1N}0Z9f}uBp-b?&#q?e8X0Ceee4d^7g+QCf682OG%_g0<0e!ld_ zwLY-7w$99EdUVw1WT(4zVZbX}Ip|eDZo_UviVbNI2JRaAE>(i3&xJsKR{sn#(B}0U zqa5R@ZGTun-~R`WdWBhm9rZ;QroqVEb5pN+5(ApZUs}+70*XPqZAx=tvvElL>=xEj z{)(>fsn*EU zxfh9L1g^q8gyiHt)$Ag9uX-hxnqY5RmWLBHv3-1NI zk<@klyz=Cw`0c0f-fGkR>Q@QmQ!SysU7JbxtliBUBh1VgHRiz%9cO=ScJ+hITbeTg z*FzWWdeWj(*tPc(PP6MfjV}9M43}f=V>mUYTFLfzYl5gKi%%VU5l{2WuJ8@l!To{v zf+qGtI`NVhGq)E_V^*Ffs5C#XnP~6WX`wySu2Z6(cTrk%a0|Itc5`^|Vq*ZFP&|jm+r#YcbC}Y zN?3-BzR2GiHEPzmx;s0bV(%>KQ2F(;)8)2^!6RMLmCk#@8TrBb8YX2)|85C-qQd`% zM?F2BHpqV-&SpWA(wY6CN#* zXC&n=z{B6;uNXMr8s4(kFIC>O-rL?Iv%Ju)AC7UBw}3b9s}-t*9Y5yIII6v|F$hig ze5Ti5enQ11uvOzUOWWQu=$NmGntVd`008q4RTb;K!aT3wkCoyF-h5G zP&)ybi60e2jrmNfF!1?Ckn%(X9n4wib-}huh{ zbc|2I5KX$6%a7!LjHGqv>=tSZ1DFTkm6L)DVrz$8>&lxKNRO|X8D!mYoR;sqGEhLd z;#k#w_Y~UX!9SY^Zh626S8jzIAFm7CS&iZH+Q9)c+R^SxcgyBfCM9fi+|24I-4Nyk znzX9`gncB;Q1@LQ{60~dXBZ*X*AWMeo8jIQ6-0CxaVQ*DV`XJ!E=mLBbEs~$KXyAh zWk{5^5J0};?t@uMA)q*elK5-2aT9%19w3()J8^jX?AnHFTx=X=p<0cuEm?vt1lvGd zcA@jZ3;FgYO<;7(Hl*_!gmP~!XrjEFGx_Y)|RfFtoY`hZOA#C@cE+ejRsn(O| zitv@?0eOqPx+FvE+UXx6dR+hc@h0cCCo$H6-m9Nn%(I1`*Q9d{i?mWvq`FNKDFlx4 z453T$bJb`L?hG^<;O4B)5BwdM$GIhxI3#3T zOEE=`#sp#iZ!3MU+7dJc&Oxcp=JhWrZ*-GLKu;p1_1#e0_&Is*VfL(RokKQwAvPx} zT2G|R0LJ|=Q#Fh*aFHe=Sjdpk*x_6sCHq6BV6A4M0MW})c6^oXEZ*T{aXmxT<0`~9<4cg?$c5-f z!dJQFq@_oYlRzr@@2R2jiZ~TB{{7IoOCn#md(e`>%DSSE1K2J1w9?o7dTZi1ZtgB^Hsh6 z@+A!?E8WBqDs{Imxi7vlvGF{gPb+*9^qaag5_j$D zx$0?&3y_aqro)8tMOopiDst|9XW8#o4e*=znJErMFDK04KVw`8EJCv(b7U^7(BXW5 zeu{wq`KL<;G9QVN%_W z5s6{l%4pSC8nF3FAPpTfFQ_%D?r$m*{4bQTyZUk}N|eQ|QkWA-_dEG*VPkc63M@25 zWjarFIO^D#iZY67@mj9ScGpjF2q0keBtPASuuRb$(fs5xo>R*~ZcXF81ZDR1-p3{3 zz`8pZzFB648sO380K~+=7gajQ69DQ;nOWYFzLOCJ$`>G?mS+irrdztzwX~3~E-j#v zKFr4S?)QIHB*;~S%L%FO{uKt@mzhnVx1W%vu>SqT+0e~0n>$AikS{9EgT@t#(NSZ6 zM5F(-FMMAZPKX8b6^%a{8h=22oBibtCfcBxdoCo@*3A#w+5;)TldY72nYvAg&V!Bh z?AmTz$g0VM#D-p0jzoH)7j<3oEFNs)SN`kGq2HL_a_1+jgrUNc8uODQzN{RnQICL6 zvL_zO9-S9hPS1W9(l**>_8(ajw%AiR@?p(YK5C60%#pg)j&4B!UQ$gym{aSUaVdVr z?h>oOB&J(AtpgGTq#Bk-;9`BX<4otH!^6Y*f8VB?K3_P_ z?R{Th-E@y$;0%N>XgPt{besh{A?E`CmpBO2;aF~Yp?hX#X*!8uG!EROYJuA=$C*}E zk^KI*A^jOD$0?CK((>~D0GjyH3FB~e;`%ZzQ2Jqz&hm^3j>)ks@rXEA>Yt>$kEYej5VUZ#{HPr z|FGX#jTQ3Wi3cpo<2zNi1QSsKa~_e{0QWizH6_9x65oUvcV&-&KHJ$UHiRwYDKxF? zbA5%q9{yIE;uuu#Np2h-eop^Fc0^V`KhLQq=y$hTePB=O(h?RQe#~t{B`Ksy*vk$==iYyw+1rq_5D={s zkGs7%KHxNoBBK1kMTB(1G=wov#mLIbQ;4nPB@~|A<_z3(V%eb&68{UXRnVH?hS84 zOLaDZ;mTnyj7Sp!{L*mD*8s}=ylrkem>u=4NX)72A0WNxB7|9#><9x2X7xPpjs7eX zPa6wd#DC_vYL9T}Njyd1s(rQu1nF=6J}J*k`k?HL5FySVztprr_^-TB{?>*o@KQn4 zv-?W3a{N}!;d9dmJ=_R3YG|H%e%XkR9C+`ZtS{E(yL+2G~3kgPka zx4TSE=~Ig}26!Ti5tnZfrQden5)yWMgF|^C))TOh3Co7OF7yZIE6Ew7NVgN(Ki&npPY(7o7!IOzP&>b zPU%cf9=!ARAgRQ`16g%VXw2b@J~SO=x}TbpA?O=iRQci4^;4UHH{$-BPkAj!OV37| z_b8D6jDUx2?zQhccT>+Wikw1CeBK`&zLRDodgEuQ2gMKox=*RL$O;GX;P9i z0=B+6LpKzapEI4XPtw?PMnb$I5Ko0@j zo)ro-oMk5qAfI_pX2@3#Q9t9_LvxIWEhhj_5x7md)yNBmu#-PEB~O};t?=1O&gi$S zD!c#XfE^{p=j)O(KslM$U;$NFpPRW(+sVOU`8Q#(6`P7={5CxP@H|a~OJXhIU|E+A zAR@osGjUP2$M}In=b|f2CQ2&sQd@}ev9WXBlUA*&#}C072t%*NT%=JqGn;&KiphAs z_whAYaF>grO7oAbx&TyAp<`dFN7eKyWV?;b8oH7>cjsWuzA?BF*NAE12XX#=Ie{j! zCLG@mw7<1|g$l8OIRzm{{+w4RHDpe~ht!Y6Hy&f0zqY*UtT8QksCart?(^v}Y*VZ~XMJZ%bCA;1R!4+UP0&@W>H8#C5R^Vpwd zlA=&X=Bxt9azgAD9?e^1e25xk@8BSu;*-<7;H97p-))czrTDO_k+$0zcyz?e8&+p* zP8QGgx(xrmpF>#|WBXQ8vKfXC!}C)ytZ=mdCWz2I=-3sTBMb)Y8)kf)a4${|64@B;tuN@c=P%r8 z$lB)Sac^1+sX(Ebojo2lcT5GD7-Rxi$j}bfSci7*DzsAeM+=4yk!lo>X>mfF?~0Wv zFjNA;6NQF3v=H;u=Q}eZY~ng<==Y4VT4%4|*+DR}>)q8Ltwbm-Nn)N(1nr|I zxL@$rCD@H4vfrc)F*oOAyFHRW6yx1JkflI;9mz)?3~1T!H#MYbV~^ykU%8m+Q8zw9 zJ>1q-gN&}P5w2x?T19*_>mwAO=t z4Qub4G#<_FC0O{he41q49{U+%NPD3wIUP3VJDLQHv`0f>tMy1P#WhZbeWD?yF9r03 zIImshOzEIqSm>TG(1cD$K=l(JMb-XQ@|m}8LR=BhH-&rw=28PCJebgdAfT>Ear z3|UB&E5bT~MMLv~knXdx3Gm6mNIeA~>1&>cNI&NspZ^3YTFo>Y@+3ZNb9raS81lC| zb>sfM1U1*#c)EYZDMPSzLJn+uA<}6mlTa0(T?sl#_o>@~xKo(9(9waYUjzfCvlos5 ztMDJhEunUZ7LHebb;5uq4%)|~yynPA^Mj<(p~GXpNhkCA<2WT;9R>&5&X2Jeo6wzA zZqP-{C~Y+@Ex)FL7$K6jB9gS9=Ig&7(Ve(&FBCazzinY~`%59D{6#u^P_8r}t+@+e^-u!>$$_Jjmr5l>72`YMB4SJUXd;0)UDsU6;D3>Fj0su56ymCw(-;a^1r}XrTWKpm9xco z^ZWU)HPxjy#d^k?amr~yH}aEBF6KOc%#Xd<*%zHaGAMk+JkBp+Haps~^JbwcwxQoK zdtI#|S$Zf&L2H!+w|Jy|QfMRg6lBL#_kaJRS1MY;vm~ZfEWAR>SN+q(kb`}gc6;U7 zQdq+m*2E{U-RnLKrfc6PWu0B^sXb%@-}#n@Cw6+b3GXdc?qEy`7@Yt$a?w74<_X*E zJ9Jsi_l(Xey8_E!3vqc@j8a$9ISk65_##A*OGk$<5h|(i)`KimRbof(}`4bJ?iD5njK}g9!s)&nunukzMa%ijem%*!>1@u1a_jCICK7P z=(<{~thDm1CtUu&@x-4G?ccv&ABtL5A)-PCPXWW}@cY*0y~G$PGPX0#`W2`%SpmaG z0Kr8!U~&S0M14Zqn)@1ts~9?2sh|XtV*$@wHUVgPK&4ErtsSlzxdm#3ywC^%j@MX> zrf(nr1g`=aP25vG;!nyU&F^*F%=yh7r=r5Bk2j-UghJimy9ZBItkvMMsZb z>0Mtn`x=*$yKRhtk7K)>%n>Nhx@Vwyw70ikTC!JD+jMRYYo4Y0`pFTe?1}k$Ty$;P zYJ1;@p8$h)d|lVzVdrJKB!&^Kpb*=GEprB*%DFuk(*lF^UBW0#OA`>ontQWAT)}?H zO@abJ=qQ&AP{nS ztor3OJQdGReiuav{-_4lWL8QBCHOmY?j(MnS(Hcs2AhKk1G^6!0FT*UOo>hCz-FB0J79nF1XavO z70?;IV?ds&0B0C0Gb>Omyc&hN97!iiG6(G>%Lcxz|RC{mW^nJ9+>91)FFk1 zHMpn;zZ-loKb`+Q)J=5g8R;~$3VU2jz z*)5Ld=H|leU5%@%;#wG+m+e2^6BtgPeza8-l^d0{s&?o(#YIkys`fty zu2e|~(6*2M@KdJ-tHGOg=gDUFG0wbxKwABUd?%QHeRVtn??D&^qYDbK>rKfA^ehkg zrT}3^4O`Mb?X67eus_~H1mv2Z1^1()xDjMRLc)`h{YjW*D>eN1V1NHeptKO~yfY$# zu?qZd^0x{Yb$7srX0C?F7vi@rRb4XjLUy%3lkkV1{OIDGWNs^!fKL)5(zFo`k6!mX zg3Be`LRk3El6@{X-w`zP-^i?%bsG&&O8kye(yl2|_X-O^g25BFCSaWjQk9hr?MI@| zLcGHMWF{}_O5_CY_Rz5e-p^_N$MIUmdEgKjrt3V(cXIRRjs^n&b$27t1d#|$S9aW0 z0#*75lOy9p2I8YVNdI+x|kmVN?Ak6PBs9iBviKrpVj|NmJH3Llpz^4Fr%I9pfkW2E<(3T(IGCB4*@y3^o%qM0dF^|v z9re<#qd6dGQWv}lFw#Pe)CGomw9_@n;Rsi&va&LGwTjeEhF2}z(J!8AeH(UfIq>uP zMWrwx&E@ahnQ?`;?4~3ENAv<%s|O?g*VIbFXJ-bh7uxR%k(L}`X!nI5*|=<|3Ry4a?(_-#>$G`+1y5@#9cuqrBfJGnob1JxJar*KgsEpZ6tX-lWoR zc{i+Gv(F#=bt$XA^(lANl`ZqVzQ4{aNeAvuS0>)Ke;=xk-@gi-e!ZS}-pp=1&#ns6 z`b^AeTDG3>@9UGFmb|1Yh}aX!i<{mF!Gyt-2c}Q<2@i|qEYwNWi0U%ceCma7=|r~V zk22By3%wd!-|wc-Z9Wa1COy8ISKA}f|GIF5lDI1Q=7EJ83HOta^YydK{~XPCy7gXM zNS1*=zE$$zt#o>wh-2;I>yrY#Ncr+w4pG;$`q`aQqBKiThiElTiKj>1ue#d`xZc@u za~j^)@XkdW(gmA8M!nmaceKE={F&|^G(Xt5_gW?Oyq-zR^-e?NasSxr_p3=)^e(pS z?FB_P3ubD4-!&KzCEOVf>iFkT^etJeSGW6Iz%Sv^Ecj_Le|z>m6yHj@)(CZ@UxrW3xITA=Jc%R?#A81*0-%z9Y~iAna#bes})uUOb7 zY|{fyB^ttHmF4$}48vc|)VhcZ-gS?M&qVd5de#j*=^N9x*3HWf{A`k8X7!WHhViw@ z@24T>W>#*VPdETfOwmnN?tWzPW`qK^DX|kZPwhU1-3!V3Eb;Z9$FpDq2|~qyJY6~se;gh{R4j~q`w73;M94+uCc!sNUde&H3fx*ARC8;J_39ZM8Bv}7UD740fUN|YyvP)fFK&*0EK`#dk{%* zsK%nUwY{()2x0pO(UmFGM~21>6zFFfm=tRdkC_T>BpI5l);JAW|9T+^XbZhD&jay_-Lz%WzJ z3wKX|BMMX^3n?r4fP>Qe7M?7cx+8&vOmw|(pQD;0IZ^=~i0rD{JhS}CT;@P+J|aGB zc?s#+$_!x?bQ=2VGf%_dj==g4IAzG2h@S_OwV*9^KavW2*WY(XF!p8uP&7CSnInp0u)AEzs%4fm|5`}1Rv||XnE_2jhTqt5( zGkiFq&>HlQh9Ns(rvVsKCP98Gee^*6)Yf0HXXTLC216{S#(I+R1*!)Wsb`3`KzebjO4|5~*&(s_(W zP=`Te=Is0bqJ?5&SE2A{jr(}&_OVR3HwlacI(G}R2W#AMP19h6Fs~FJMg-IV($kso zpU!9zovA5-eB3%~pD9dE6s*jgBWMyoA4aDp~MYvv0uduJoCx1^<2!)7kf8&z~@Y3jZ@ z#`?QD9)YVD20x7RKLX$A>>HSuPypghLde|c-5!uaHqw>{njTr*ykuYvGQ5^!0n%AQ z0OnI3t93_S{`8g8+g5k7w&T50nJ@cYa9RB^yJqN9ZC9vKIX1`P>170-K?nC`h9&%8 zR!{WK(ca%wyFIw~WLR)!}Tw9(Q*r%8&d&dYAgR()6R-_%CwiAtAwg z$zw{EKW2EyE#*4b+z1V4)lbbk5MMvPGRuZtCJ=GLW##Mrrt-yqo;F~r%k~tyr!M&U zVWZW9UY|27n|fn0I(f+((~SQa1Q_o=YI3bD83rmw)4m5ju$iqO81iZLa#1|HIk(K% zQri!nS33)8c1PE;`-E;x*m;g2@2Abp{SV$iA-`Vv_}J3jx@Rs-TVoPaf%eVl{TiMhpQ~LY++wFFv(NNw?5~)|Znq|Ix z`7>G8o;_!Fe_x-Isn4=ZN{J{M%?1LjUBB*y=U?pa?VUGoj!04!^pccmno6KmD_3pU zxN%Z{|AKjQvpjFNvts;u%91oClEAjg`$2=cp@E1(h+N9$%by+@8J#(E#_U-$)azpr zvEUy`ea~yvbI-Ng?MeNUrcRv#dYvM`(D3lu)oYtwT{C7(O;c-YgDYnuL|nIi{fd<< zyBp0#ix#A5qt$AeEj&q*BuR<@S)R#UqRLB&;?=f=(hwy{Dg?gp;_By~dkz2=EnGNd z%H(2p6xXarwJy|1IWv-E*2*MFp~Q{|0M9)0%&O;}pE_mgmRoL?B&k{{8g-DOCWL4< zn`7f+E1zAJW%=ftFKRZMY7G|axbXgjDAF{^b6WB2is9kmSu z%Dl{2bzF@)pnKNf7t;n*BX(mXL0Tq0_>+lpwCBuQ#*3{hMu$(xO1?W|3b)W^aBK*<60ccX;hXP$Xx zcz9&S%oz*j%~RBru#}Bb^8x@H2M1qx;e|%p*le>!iU%c}JkJ3D0nj-KsTR1pKdD5jbS6oXHLKS=`uO8{E_c~!$KJkPHFhbb5SS#5gl!Qby)6g; z)u-pUSh6fj)AZ|K|N8e&{lUyB)35*A4T}~nB1y=e9+gUCiHKyL=Sh-?G+DLkxee<# z^z`&DTrdw2g?@2Wtf)~V;>L|btJkbXfH|{g_4M|%+nJePwYZwiCJ{XU!VBxyt!p&9 zW=x+pdGh2|tJP|?q|65fCg1S4zkT?FAAI!DN5B2;Z=d*yPjq*6skBZak|b5$fy|8i zNC_BbW&xq)oP9Lqd1~PG{SpK)6%a9o0BZtB=aNIy513>0?N-JAz#oa+d63yXL~u=Tc_M=WkmdY`k3s(suOJoq?$IF;<`=+hEsMtdn`` zMA6>J6o^0|Jo~U*@xbWz`}OqA7EEhYK!JvaYcf18Ot?{m6;hU;;^c}|CxxLhO=MIa zVdZ_<)AzP^eQi&(m#L+C`zi(bsZ(6N4X_d6&?QtmD-ot3%WMjp-F}lW2CI6OazX_({QQeN|@fI+2xZY$;y?h ze*BXk|Ms`PeewAh+F84>V5CWs4nOJ{8x$x&G@6aEv9W#kJ^WX{I(ON!SyDyw`V-=439+1S%?Oje28D`k^#;}kw1(l)+gq)%Z0kLGx+X!Ekr1Lt zav_v%f!rm;J#%p4)s@1FY6i-wFj*Z9Yw}zYNj(-c!y=$Yqfvb17*R@@WjO(<4?~fZ z1dt?&qNS=C&6HB+l0@-VCMhsg!66alQnuS|BE1JkCmB`V&5TQuieSU*Qn8ezNh(ql zinmNksXkp)u!Ja*RDHU;=sM3;peV4I-Xux%&@z?8P(Ymp6_BQ>5CVv@JU2eHU`xF= zRT3a1BGG0Os9+vcIIX5kN(m%IlBx(_Wx?`1$3o8vnn{uh1fCU*@=q+E4X z#tSsVOt7-uZcCDh5J{2(fZBr%Ivs<$s7oXX5k(@*NwcAvaYgDK#fBxthRK&96uN9u zNTjsQe(_B~RtTXdOG_Xz)2I^WLZJFmm14!bnB1tel!Rz0l_IW2s$`NR#X5cV*%hyU z;~Vd|Iv7%F z-|k}^ecg)GN;-kdHOhowUWDN)02#bgP0(*1qeG+}o^_@JDsxCWt@ufw!930k5}Db=>phGk_5 z3eY!6J{^b;%2!aIPW=)2-1;NsqcbQoI2NB;hu%#cZa5?IspF^&^ZpX8VeAy_i}~G% znbp_aV~+CT*%`w(uM^$E%TFr$k(xfbbQpG_im?Fkh5(?S1}a2Ud`>r~zJa87QnG%v zoHGbrlYuf&ISwq|0bk~*5y}lGmXyhai={^_w(|%;^EGLrJePCl&iVGYzJ1Iw$6R^! zl}|qLRJ+xdGB2WUsa_5SBqf^_9kv$0ioNVzAOZ@JAYyBDeBT3JId8!f#ZM-Q_&1CJ z0QCnb)r)`;QD|9MpF$v$3o+hVe?b9s>YeRS!cs;f8|2W2eA?`!*(-_-9p%KZF6<4K zd}aglSS$)h7@!%@O?d{Vh&fGkT?WI*DyZ}TB9fH3BonKfh|8Q&;h9Q-$vl@tLLerR zNPrH*(j<|xh`xjg%IzXh03eYJF2|xTKoiTUM2XH~APP~u`4%OeWlo^NS2bic z_FU#_bCA7*ptv2PL&cH-B9gg80D(efj!Z9#1!F~_-~x-cVe2z0k)a;e2KB|OB;i(9 zpa7v33hC?vsMlSqZbBuNC7DHxNbf?_EdtVW3Hn`xy;`7%)qE8%PD zqLGi8`aI7i5h5fa0YtWKO(3YRyIDgs>Y?=6p2R|cd7fvoZKj^JBE9RaAZw-!wC<^s zCTEoA`GN%tk3Hs?r=ME>lb`(LuvZ>-;6Vqn4%N~wN0(^DR@;#ZtuVqXQuGB1&vS_= zIVZU!kU1a%K!kFpnOng zE590$`5D7s2I*J}$Adq0BMPTNH7@03{W|;%*RdEie-y#ytb#i(4)+`hga(cVp;QVN zc}>$}eUaJ(!9jpd5(m9U+aUmq%lV~p7e>_=NJ6t?dN&NXLvyG(W9QoI)NvHsC+o8s z(KKXpYMNMLiFM~tsXt8>nj8#X__5!XpJbH-^QDbgfs;6Zb);nQwb?zvJB%<>>ae-r zSrTONQ8h&8;|iXvSlyaE)IC^ry|z`17&t-2hnq`DR!wyLqvo8Aj%PY*2xyh_XP<){ z(u?00S!ZimN1x4h7~nBU(b^~MZjG_xH}wEjaVib*Ttzz65AQ+6ECJ&e4vUM9ETeC8 zZIsM9qx*VUUmqPW9Xsl1GGeWQ$NtZnmw;&}RI=?b=2+eiAmyc(ip9`D=Qop;))?hyE= zwowDEctI3`q*Rd@=|`$avkL}x0obma*(@~z6>I9mI~PA=0ufMvY{8o6qy(5cr;()8 zY7r(tCSEQ3AOPTBVe{t{!Li7ozZvg3X2!F1Q9q$(H_>sawruL@5)w%s17W z$^#@q5Q?M({iI6ls(~T&Ob>0t6;%w?Z*H==3Z= zh!m|GFrcX=j6Kv6bGE2h)BuVw#LyDcNtq?#l}%!n4PTQN!g=4d=2N7p zwMBwmvgnKA)xmbCV{__k6SGI*1vvni@CSdaIsll6#B@;7AwU7bc^1=q8cuVlhhj5e za)@9@K}H!2*(8ZPn#8Rr0t8+tUSa{m23slM&$T|*Vl0M1q_?~g*sLlBwA+2}```cI zLk~Us%(H#{{f2o}QwUM4c|?IiA|=i=70XXappd86ms)g`BtYaS4CtJCVKj|ZDV-P! zm6-4A@7reE?Ot{G5wCm0>p(!B=P1f-4p=(iNqvH@*NOZc!&Ti^MX~2AN@qgQv=TlA z2*zvHL{?!=IC`oSQFOdDMd}Bu6`Xq+oM^=m**XlEj;tyGr5g&yqCS%~I>KtkIx<X>w7VgDaQ%hRy-*R?wc`UGwqfC^-Lzzo;Mgg4i zjT&xT;Ms9I4pUjDmG|Y(+~3;eHKBLwDC7}W&=7_*J$f?f?8DrPev=^b>&RF~m*!}O zZ*w~~@rz;AuNlds2fFUaM$Hl&pbgF}#UvtA35;TeX4jW(g|v}Ts52NuXOWkB5)1TW z$}mys`D1al-H6DuL&a$>x)2-njb_<3*lRmPuINY|>zR{MJwE^>Ad5iN*495I|6GCMJ;jwIC`~2r0SRNkr=9@RDbB zpa`0OPGg7@w))Q|9`CCZqf$ zQe6LB{_q`H{}i!gr$*YKOj06rG$}xNUN6Ki_A^*G0E#6F#d$J+6+cE8W%8^Vw0VoB z3~oCW(nbFSr>Qw|R(cVK8Qx2t%Yl}0o}E6>u_tSy=ovjU@qB0?WZq&laZqNgwC8>N zx&hgvSCuSU$i$^CYMX{=B^cSA9d0XFW{1y-sa42?hyB1vI@-sFT+UW(;9Bh?YU5(y z;5`OyEXi+Z79-C!1n7nchn+H%d(%qk6i@U3Ulqa7!e-FLm&l~-cU=zFSPE|pWY3}p z(=-K-D(g4exur@>UR)b3ccxz>rp&QVw|L`{s>|26RvzHASPApT7&GgwbQy*cBcmz= zY1#&=*pkU7%xhxAgT)iAY!)2du$?|9(g8;Ds~jyj2D2WJw)!#+YsOm8K}+4`x$+$ z6v-C<2NbM^C@a^RLpd-6ur-Y{AM2U%%*X4Tcl$yX!1D8af`SScTy<_8TkXwM*dEar zi{I8GQQ50yFKp(u`2Y>U3-@R)6M+!;?8EYz`$x9lx3_<;&=3_63nvJ?sW)C53R;W3 z{G=!qkS?Hk6x>4LY#ZWzlCMZ!>j}Kizm5s>=>)XdX;S^O24fL+D@{_zCjRJx-WTl* zam^AOa)pNrvVqv)rj}zB9Qvgjd5N2?IB%Ub&J5N$?ZiVx&6rGl=U1n=)FM+>I@@Zo zXanp+&kpkR`}I+kK53(N^B)3$`YwO~^$ZPC20eW+YhJqi(bfykWQ(>kDGO0`fXrHKoU>@0wy9U-ntqOjnp#1cT z0$UacK*;azI~KCs;^ILo{^lLslSr{W3ag&XfbzMEx|&VOGD^y)F|-ssNEw~)XcnRT z2LL+nd5shk5;%K&oH_upwNeg-^hevmwRAySpynLM1mTv&Y=Oys1fWB}f>ki@Fj8v< zJd+X_PeIE!_3knPR!d(|@n~iI;~{cw5(5h;I3LzZaD`S%*rmh4Rb?1ZR~>`?M29=2 z87vd1K}MNeBR-U4UrjJ*V?Fm{x$FYpxnzmh6olQ47PsRk%Y3Kh0RuygnE^JeaM%8+ zf}m!et2C255aqIXMpBraRRAoXQjA4qf&#s}QV#^%PW5uKfrq}O*6GA)u2ZP&}r5KiY=E&(0bOsh8&?C|+++BqK z^0eQ@L@={YJnA|s4TudNGx9bl-BRM>Vs;vqrO-=$*|3ii~4MD zM>q@&s=7USZ=fD2-_JdUsO8r2;SBq&PYrhH30QcZ>3Nt|V zy^%U?J@AdOpPP_!a(W4RU(~M~tnQ_HM8u3l7={B__QNYWoYg85=~qb^^|JG&r_Y%4 zL9i8;x$gslZ1@6<4@)gz4{kU)6mD#&cs{jco%u8{1BXHm;U0Mx_6j#Xr=Z~QQ(8wV zo-vUq5M}YgbU;M)t*M#wn;X`&S3WT|Fs*mW9F$V`U}7sL5Oq>%?j2hIDq)erz(7b+ zy$=UKhz@a}08rvVAgO#&@roNn1OiDQ0?t&6#LXik7go6uM>qHOG88crupKs5wnxic z2mlGsFV9v#mo40)F?l-X83B;`bdJd{5Zn91gDYEx_88b9g859khZaM6SPDiTIc}%G zrsj1^WM;dp$8AU?sanLYN*E?0Qms`ZJE-#p=bb7S2b7;}0&97KHEkV}H@Y2L{*K?X zz7@fvN5fCVI8f1A2?3bjMWcmEDCGt(#R;~I$ z6%{)bXW#(O+EHkYhNlSZPH%21=nJ>$-diG$0|FO*+h1x(o!E z)1%hIH`;HyC6|ubF64Mprp#urio@c~L4YI)63Yfw`)>&7i}yQBgY>wpF>nsUU53Dz zk4*d+Y}1P0oykNvn8&FDTFAkgj6Qf{Emb=>iePQ@-crOH*~y(Sn&p1SJ8vi(!)#k< z+d{H4r33VO4+mB}M)2bW^C_ZGWE6EY{G@XntjV`i#1sj=I{?Ni8$ods5({49^aJ|u zaN9Am^NDoQMl(Cw>az}mAwX=IIt*}w69NFn8iZNn4kR`-#u#`Eqn^z$!x2MKcEaOd zJ!N67;J>^xR$s0TK&jtxmi+rB)rFxQ`;5QR4Y#q32iRh_ra)wsG(L5Pn6+7xP)aE=0ZG!Nlgj^Xx8zvTSh@$w zVc9=J?pHC$GEfxDzG}-*0y^t}>1QDW9?_P=aKN!8C=W427B5;o7Co4Xku~M&T&O4o z4>x6OGA=^Ak-q8}QuDtV7V?)+sYsQ;6`Xy>mI;3i6z8+F@Fm`Gnc<3Mny5gd+Prb7 z%HG0VuwsBsFz70}(~n{XJF4Yzs<5`LiuQ47(mYsV*U`4qf$uKI=x1lNY!N^|OYbed zpf);@Sb6Z$5P;5vgf2yXmXfgtSAcyD?ohv7NT(@Ev&8~qXCBZdXER!$|I)+zGV$3C z76(C@mkOBxE(lk=f@~6^u0MhXV~{j_nsplFFIYHz#z3K;k=5OqjheGiQENh0A{csBQVxXxC6tSXqIKwZEN)&~ zo!n${6D#Gif(Z(l(J*MLV?}lQGaR9z3Il@YgS^=*%!hfZq{^-_@fJfhd09W*sJV@d zqo$Gc2vs|hYWo}X1cq#7#?<=xviL>tq~lbGWsXBx;c%;(R?=_l{4{MVlG#3cF)>z9 zh~=b6|65?C%GFZDm!`RiHYzhL_AM0d9Vr&tkoVYm#n>xCj;Jt=J~$CGGpoRa;Lc5n zu=N!H^m$O#n23xSBaOJVCXtR`H9;N0p#ED35doz2m?!}t3Xrm$$$=S-1xxZL?;Bfw z4=&!hJME{u4IsdNudqF00KnoosX`u>nQ_`Gmau$JiX_mVPcbHf06?lqAqjv90s+Y) zxl>|tA}K*&@hBDNeZzRom+7@1(eDd>Y8y5^XgcR(`FdUn)>rymKu_OTQ#KaOj8RDBmHfiNFya> zE3?EiSDKvB=fYpI4Jk@H(NUVx2dZYlkRnuc?`HVru*@oU$Do&w;-nC#V<9Z(R?Z!1 zeu1ZanazVhKpv7|XhH(!OApDs_Rgd?)Q^g+x=#5pp_e(>(neUpvfk-}#fmH0_nuB? z87ig?!Ml8FQ8DIxlj%AjzUfAH=(Z}|O|b`|WV2*Ln~rkSX1rP$CSQcbYq>lQBf4m# zIGZtGf?+n2Y;l*PEBIpFRBu!(nLuQYm8{c@$#Pj)6N&aVH10`D%K?@TWxka;J6A^B z8ItNYHUew*wX3uM70*IdEa<}Yl|K==e3c0k5dQ$$FuvLjdhd zHMuIlrf*q0GOvDdlSu3<6Q*;G7;P|QEk=uTVj$S>+Bs$m%JoYX;bg@n%eXs>oQ=(y zpz3IpJ5R8nNOYv}5HcW?Kmh4QXP|E7aJO7kPzx=AwePl0!c~)N}_h2Pn*-#8l`8S7?*d7Ep}*hPXUskJ_e0pwmoNN z*h?aizQIu7-IRICjjHUIi$P!LW5gjNU z`D$A+7A?1$J(sfMfql_g9fPkYs>B(A0cfh_Pi-g)t}PElp_NJm-p%$(8$#YeWEE>i zL;PVKzZSB;jvp;)qQ`m+AQrP3^}7+V`yCiMX3C=7Q<0u5)|7ru3w7#3Rnp5`uTI^i zdk4D6C7Dkz6}X2hpnj;T4Gq)K^p>?_(~74qLo|$`t+h!; zvjny_nlmMm0sSbf#i*~CMyi2W(;bBaYF$=C@Y=p=BI=UE&@g$f1hgTyyDoRGaq=FK z+OSHG)tX7$#05iW2OI^G+9f{-;IA9g(?m^m75a*$T!Yz=j;-nhx*bap)vG!3=$HgD zaARYmydWU$z=Bub$I@iiub^llP>2jL!~@2#WuUedo$_o#=cWK|VsWDJ0$Woh4^Z#L zPcEhS4wte9(`rvoI&)q+l29mT=ups1bsAAU(fM^Ka^7F>WM5TW1u2JZ1siY7Fqg+v zrbm5Bh94LGu^f%VZLw5CQgS#Ci@HOMR!@h6p>d6gGYj8nP-k`RT*!Llg0G*FF$=J8 zC!)^*a%p?^$h?o!hD&9CWq_ti>ZF!DZ7?{~TNtrhJ*fF`FFVlHB3rXmr%`z|eP1O2 ziF_icp9h9YMTw>!YOA2bPt4pn+8^w<0$2nqcVt!7Q`WcOH2r}LafVj=e`W7?4B z*$r}->@0`q3y-}xSD$v47t6nfe6|CR`Iv0q<4+tT?PFzjTQe|0Ms>lHhQ_e00%y@# z{OY=f;$TUNz~r-pzB69J2?POnVnMYQHzP~OCA7^e0qc)SJnH7SFKvMBsRzW1!Q&B|tMOcQ#J6S-{z=A3m zu{bjc#@8_g)9Qraymm(b3TO|(GY_>_EN}Ns7Mm|?4onrf%ux`4P~0o?V2h^w6$O_b zG^S#F8mt*BXrxo(<2`xU+BEv{fyDrKUi7R7F0Rqj{|9rIMvy@pJOi#N@zxr5MI zm@-s{K3J?@H$_{T1ZFp5lK8eOWzf z48UF*YRI}Hod^H`>yD#-J0$ZJQCk6)PL{U9;i1&T@_;R?G8iMr+Sqlf#u8lGvCIp( zN?=_yHY3(u1sff)t8eNlf#JmM&! zK*=HV3+#1roS@5YdC97&E^-$J_H_6f67#k!$yK$od=BllZizY2AWJ^y!QRXBHb1p8 zL7^Qw8p85wrELB9(c>y0F|EUvp$PE~tS$LhZGUS?lCLj2kRsm1GdnJ)$~$>i93GI9 ze6moAk>dr`LwLWB8IU_b&oM+h;3v`ArWzs) zB#q91b^D`KMR5Idd5x_Fu#VZ6utKr6rf`sK-f1cUylTu6d<$M@l&S`(G%Qu9%hT@e={BflbB15~)*m&M}#9yh7&8JrS%1 zA*~~?KT|cxe66dzBJWE}PkFYIOUExq-L$5oxA`f7695SS35XzxP(T8hXA%KsZ{F3_ zlRWd#=zX^g&EK+n?&gi=06+pHJ&V1;KvJq| z5w$Zz4VzDgU+ zPdy4mwk>3wTg^M0zD}hEKb}elRDgo#CtlRRu_~PD)Z8i6DGz={Rd;OXglxQ@9f-l< zI_bH}si|@b!Sxkfk2VYlEG56fgo+#G`pMd$c_RCrjlr=STy9wr4Z$bq ztD-f1I+c#Lt|Rv=S0`>NGrn#Kn#Dj)HI!@QZEN21ae7t9M8!=F$9vTFWbRmkp;Q?E zL6J)32~ovQjDC<4o47lBTFIH}6+`FvvpipE#9b4>V?yudK*^h%GBYRHz)L%71+-Qv zGY~K#A;SblPdb%-F;?D)>n_IFtRtG|F=#0iq1JxUv1$e&x$UiV_Vs~5M}t~qU1 znlwlfiNu*cYk8K3;BOc3hg|BryJkZtO$*Ph5pU>2e`jF@v0Csh83mQR^8IL>O|)kx zHdxLX+GA&|atG_;72l!;xMClrGHeB48Isxmg9^gbGZP0Fp5v%*BO95p8&AuSaw9$srp<5gjprU!g8efO4H0E9*K}cj%po z2MhZ$nzrU@H!6a5w!vW*fkqr9f^m-QjG@+<)fkR-CZZ#tEiFI*ymeMZfcOfn0nQz6 zZ3AVkIa`><{#nMETSU-^M!S_j=fEbSdYBa2>`?j`3jw*KGslh+A_9>g8BI8I=g+vk zB;ESe3jw{YF+p@m#1~&+-tF+%K@+azP2Pb4PqZ&}E-~lZDPQCESkZgT`T?5-Pi{st zk;{a$V~c{50xc`7c2KIi5O()#ysllTC>G6lU6{?1ru(PB`p{?#yzh2EE(V zFIlAF(vc=Qzbq4=R)PWCoGR*_I)ESq0igsS0D(X`NF+&lqgQOUtT|&&W6etW;?vox z$J>1aV#@4fV7lm;gh>MY)6C>rNYHjG$}j@36pfE`O5dygN{J+)c&~y4Fi&p@5TZ!y zpdcX-1eShFRhLB*$rlMa{UPrfB@lqjsWk?}gL1br(9-sMs~;yAW&)gfLS~>i(8^4(j}R(< zzQhs;HGyqz1vW2eXNdm_k-Z34xpMldZ>pW8|%(Xxt6n4wM!6%J)FfDv&nO@MPo$2lf6k~jLVr;@ehZhJd9X0}YB0UIAac4$NFBL(xIgIgKN0Wp11IqT% zU*Rq$npTVS=F2T?D4gT~Fyq4VGF5(D%Nfnejhj~2xjPe48Ekb{D}Ri>w{%=Ly0`qo zB@h<5okzZ5>P3hw;02H2pwq5;Z9MVS3-)3TvR7e`7#A^-=odYd{9(u*y51-J2s;2z z>*z_2;6HEvV_S$R>Jzjo#0HP%{}S+3^Wf)VEAoOqlNAIj08RazPOx96iy*@t!BhRN zgI)M3PhP3!atBY0LAEf~KLf=*$BM5jqa{ zW-iN%@k@R&j@|O}zVY&43+VdbZq3tC0}vugLI4s1Bo!Y87YH&3DJ1~(Oc4tQlGaGF zaW!pNm#unoe8nS_2$jDOpv6$W{Y99^vDgsMI71>(U#2zaP`$U6K!7NWhXhHaaxsJm zC;=cR$Wv&8q&6l%kP;;Th=Nc6CL|I-0YaEp+vsgQ1VBKt_#=S`BuD_@FAd?-r@12} z0x2QO36YvTI50g~Fs;!y8Pf)kkP;-KNX(`lp{1>UB3WkG^{v%VCt6THKo@T7-(oX{ zJHCpeIcP>TW}i!U9>D_TIXgxHeXJ@4#={b>#s3OSS(WQ9ZNH#)>iVzwMz^yZvG~Vh zvK#`HH=un7Ot!`5ta=P9yW*gcva3k`!ICgKeQB160gHlXp6%7SCnvQXa0;m3zhfqp zS$IA=i?{NaIZ&{-b~?QThinJ#%!sgV>iFN7*MV@fSHHxf5%-<}+Amk|k!3kswyQHW zV`w$PjB`e%M}yVE@W~mG*S2is86U&HYXxne+tL5pdc}g%qmy(FLE*;ww zgVWZ!3j$q#EyN(DGdi-i6qO8+esR4$DBfvF)nhnNs>5c7Tb@Q|hSSWK2QA8z<5D(65Ur@5d#=_T8u#{^sS|jkF6R>rv{&&w%t%l8ReBquz~7Qpg017|=AxH?# z3?lm!OOdR}fdGWME@dHFW4eo0<}p(QOfYR?vs*N~A!(=>IhQ~v08t=<%0(es-+?J` zdFi5vU5e&{VWJcLa+Eso3)l*YWiSm`sOU*)A-6K5oBKQkS1i~|kJ~N>bk->b2p9!* zf{2p7)5PY=NurT;(E@JZtv;7lcr#Z_FKjuP%#h;+H2f$LSY*<%jzODm(I>nq2m3mdKn|VUn1V`vilTFE2=q@vvznu-;!XeYB7LW0&u(I=;O3FGhy}ciSx{tjl@iSWUA{J4Qu69PF%o! zD3{A@n?EiyqClL*6F`m~s+B#Dun9E+lloowrj*K^YjNCS*Mv@55p#KdzJ{e3K&|Ld zI;lAJ0D@21ST=O3MMlKw1g_VBnVoTHg+5i6n#jS?;!wC}HLnUY1IR0b-JjiQ|IoDpERwe<}`^l z_m-byDFV!NvOBdL67RQW%D`bJ*t}y-$Eh668FeZqvjY)<1SMHbkW=<4sEbY=zv~(a zopOOY+{QG)++K(!{(v#B&TtzKJRQwkO0k|o^9a1Tx(c32GQ>kU9Y;NgN5Cr9Mg$3J z^9~|_lq3KMPy!}ENTgnCgOK)$fj$ICRBHhMLcP02k3v``N)`;OQIiBf0<~R+B#VF`lpK4dc5@P6;Ir?e*YtuE!n4c!@4|81Qr}9w@w1pXQ;G@Y|-eS z!a@DTZW~c>SYA*qi*%5lbuzn(P?S%Q0buEZ^(%jfZ|EW>f15K~mlObG^HpJyoMQAC zFBgJec%!{p3dD8UYv9(Z{s%)j4?2$LFf!To~iI7ZWqqzymYVW+OZT)M%yRo z%OuEyFG+3mz;w&6GmZkS6m%lR7rt3ITKUWJms&M2jRSW86C zvWrZIy2P2W^qr#}utIUrs~iA$9h-WDVLvTWfw6hi%9gto5^%*ay91mB*GVBcPO%(m zgtq`aC(JgCsq>C7g3+aa%3c!zGww&2Or^oBT0SCm-cv%uzdLV6+YN;ZKhD={jE*xF zbF~_)+uGQn7-dz8;=5)a_FK)vRtM}U;H*{YF!NKUVQBgzX1UVe1xBF{i0nOg4&?|$ zQI!!Qu0MF*k8Pcig(!gY2}(b(BAp|2zT@SL#DYoSt{JfXQ4pRPw4%?%7!Ewzf==(8 zG0q{C&lm&-QT)@%rw9BX=(~hKdY&x<#HEUbC(^wE0Hg(It4JtKu&D37aED=CGhs~a zxW69Y30IEgJ6=kepeOE7xgNE^8s;Z!%5p+6o)6=K3xHp(!$Yi?iK~W>HCde~OJwKf zE1CyK9Wp4fjx74s;|)&`H+hi|w^pI@6+g}xN@~Gb)~$-b#4Kn8P!AFj00^SO)JV0% zIVn!742&t}fvW@nytil=gQlw?0uUCyLn2TDfFvczL;~s;->4^~5&#ksKvIs4MlMN6 z=xa6<1xRy9)$RkSJ0S@m&6l?HS%(|F?8*d=bgU;8MzCz6n+r7pC2CQ;Q(2j2ERLmG!NG%bwZ4(*SgM+Ht}1;*$Q1#)cMidJo%Rlz$Z?3N zsDyo|=;RkHhF>Og>Z5#nx04h6*gR;?O`8&0YVkS2fu--E)Waqi>S}FM?ZL2$RX3D) zcP|ASm}V1d)qxrmHo0a3#i3Bm3fYuPKgD7lze|7NzeKe(bfHq_t|BqsLJp#$6Yr_* zP-lcilvoHyc>pS3kWY@KC}Mi$uELy7NHuB-)+7{aRJLU1RA1V6ctXdo z93!;JrDop8z#y`Rgz-$bS2_o|#-Dguswxfa2>64%RZejPMN}?=NH0ZG&XSk(F$V!3 z5#!U&y_ln*R9*N~t(2T-t$d$Hf7AR_IHBeZsgMr~8db+mC15I4sXZOp(j@dRw`;pD zsxM&y=y*z739#nON?TT!A|i_EBr`qk*$Pk|$#({?RwUl4gA<1h-`pE@3FPzYrJS86 zmAsj1Ws3@G9ZL{l*RLELmm{L!q{Q>bR;vmD1|6P1cYbrn9+CbMo^R$%KQUbj7GDiI z&PZ%@?r@kvG4+6th~c~9pmwpsFrA~}7{R$C77W7lCH<-|)k3JPH3Wb{AQ4I_QJ^3q zU@sjh=YW{$LjusR#v#L^w*Ks4UUiTF5UKYX6Cwx#5)!3)Ee|1p1dY%ErMe3V2_;>k zCv7B23TZyss9a*h#Bi3xuS%${Sr4 zsGkT^k7KbZ4Vi8py_PPcetMTzuBknB3{K3(`i73LVq*kiJ))zHoO9281k9JIZ1Wie zH`NH+*ndSZXm(5!D`YY3k$aOWh8w^Zmt!h}9P0XHVv>+6L3WaYC($9xCC_15C>D%4 zHQtRgzz(YAtwjgZSUsY(3DM1Bgq%fKbC^+3gSCdky7c*VY(}`47|{+ED}?6v!u7vn zSc5v|Hhq+XKtvlu`@$i%qn4imkLTFQiUff>G_Ptjv@rjGdv7v^H>?d@zQ^~3Q!daF zOmnzy)p3;H#=8Epb-4|S-5M*?xWLEa?3(&GDwaB{uQKN6z`>>2G1g@iEO+Jr5d9PV zll~Awax?t9Hi4_OuJ`i%mVqbNq*o!Y5l*cECN}T0o zvbgg!hKl(&J~Wq;3S+|SR4SNtq3kRK)H{NokeT9RcPz2bZ~z8qB6Uj#r=x2*mqJpi zX0_~1>meJ4Yr_peV3Qb_GA}*~2Yq%OcXBB*j~z%E;1TD?Cl%B#jNEs0T3L21q;EjP zJ8-SDr5RYf6ep-D`t6-z%j(+i!byQy_?CtiYLV*UT1u;i;|;vB5P<3q!(SR^h8O_K z+kN$@7kpGKUH@tw*Mk22t9bK{S%Wz5mdOi&+KE|*is4jn9yAj%EqB`T5)^C&!mG)D z{mQ}SL4K<1sMBCMOLb&TM@=7J0b8qGjx~1Ah&WY&)vW1Qe9zDISh46-aI5LUXIbyd zV-*<*LK27|^?Q`{YiJ~?=mh{!2%z#EU}j+DC&q2Op)f2F6!|%oJdu(TM3exCkN^-$ z2_PWRY^4Iz6cH%_Ohu9;0#Kx>6ao;0dMyu8@$qxrF|n69m(iCB<&BP*B$PFM1{Z|Y6d($;a- z6Vq67zjZXBQW{N1q!Se&b65N%1Xpxy#+3)D?La#UigXaU$$+M#$1H2sC0VLZeZi8f4rgS+M*mJQ?OF*r8C_J*z;qgnqrS}#n_whIbJICm{`HO}NUIYBmA=mdZ9^ivSVgWX&maM6`9 zEGK4<%N?3ufU)qv<3{Ca8FBUv-KT22P~A&e4|UDr^l{bGK3GIdJ~Gw9d^)sBRzl&iml~{sWif?wmU^%npDHKu zQH@}DD zLUqn_0#3rj?<>S`2ecGy9vluD9Kq{58pRO@@6oOap0vSOpV&KVbsd+i9Vd{Oje>ImS9%9xT0gRC>Y7$te z6tVcDs8f+4*GboURBTp4rNpzVKeDkOJqCps?tCzlnw}7|8!a1gt*JKFu^JlqvyLz zMn5#yV2h})$&3iNR&)mac2|CAVl27c-j`do zkM3BiEC2*Ncjw`dd9t@g;qclbQAcj2*qkWH!fM)Lll2qOfI4Nc2>^=?Pa$*%#&vs< zm{Fx5CNyVC3XD|>j?zySuwtwu*T-5@Pu^!J9P?PXevP&GC7VvTmmuB_!Z2%=ThL>41n;n~UCpkeb6wl@Z?b+kqmVNu73DRdtBbjKsOvbJBx z6L^W9BEE}Hx3jJ*QY3-%j^zD`t_a>8LdHv45RRE>ieW;JnP8{YX$Lo8G|X^)V_XHH zDG(b1Krjsh2s+j9Cc$twN%)GN4sUv4p8{IBha9M z^TP6QmZ6hJO@Z?6(O~Vs#+2X?^)dyb~$Q zn}WOgJP0?zVxU0q>dT;AI$R55nHsYav`yiT{B!oQCVs)BfsKXH3>HsTsm2MAM73vy zB#K0W`A8pu1$345)Aq<<;t+V#+j%4^%ZY?iBQhz9cPa}31;R%m5(Kq>2nbLDU?LDj z@#!fNfD%bcP}gFTBxQoxsF~5S?Wg`n4+_n09hNLYXIG<6hn3H6mEu9>kI6n6c+K0U z<#hhvEs=#}qI&D3V;?F#TAk!nv>@AXYNAQo-*+Hja|4KI-?ZdItDIlOLO9Z6J-D!Y z6Ae^ww`RQ4&Z(|?;+l{{sSsC(s6dCX#V@-XPNi-F!ftEf>qw~sfh=gt+lCqjkDW6U zRfgEiVkc4#ST$>>8l(F}On1EVYE`l)*781{lR;0knsI(94#wCdmylEcTgu$Qu_0t@ zfN^PEh_E7lixhLIXgZM@ck6oTC(d4R$>%VXYp@2}(G*O?zCwjgg~AOkZTFbJlFaX- z!v5*20Rv@bykY>*K{BL?1zI6UO#Ck9aW2Z7nNhriB1Yu;ba8T;#h7+OlW1nn?Ks65G%ul{hJC;sq z>naHCI#6p_MT!Rx$l~3p>pu1n;ZjEqoM;gsSuN8tEaA;VkVaop4#Ceaxg)Un*6jEg zqQ3SAoA|!aFOHk6&Y^Bv^0mS@$2j*Z_zOm3d-oFn)jZTGzC8*qJm-v`6t75=`>kRe z#?G5`Z;XK$9nYx*-Cl9h^Xuh-15=2nj)O%UW6kwy^{Jx#|$6dZl7rSFFU~nOLgbuNGvfPRt+dJw`K% z;%n1KbmJu>(!l8Szeea~`mK|f;Sqz*kh<@BC_QnF4UD{J-#Yoe&j$S#Z%*6T!0QxW zLxQ?gA!NT)popNYuBZv238LUSas=p8h>1%nhOL|>HjRK2s4-baTY|#zO9lRkEnbHo z?vX=*aoN&6Vcg9`d{!Mx6;xn*MVpx1n;>G0S>Tvq0&mI+F&56nEj(wT6IO1O;OMFW zn2sh2x@eqHxa+D1nw+{^Dd ztwLodvVz0n=K44Z5s)M$DmH z)UxX{X0f=88mghwfK}J$^)Sompm;7KGQ7k}>S`WV2NFVy6!izfZ7XHNFl1e! zE!Yx3Ar-F(LjVbYgb9E&(p5Qqo@P!fUApGGO! zGCY}7khMR@aLW7*$8#kG0w_MBPQ|Cv^<^wxtgIJ0l~IEq3aJsg%PMGFUSg>x=wpZp zl}%{EyDpCvwTbs0vl6k6!gQeaLS{6PzZFPFGj)JboS!O2CKCVq5mDHRD5ug9w$|&k zL-E7TAgMauwsVQ7h4rg8sp5bC*dEb@faa!^2_;bclO0R}H+jJu82=|FG!PdxQ<-$- zPKGn=`n@9DU|JAegi#@5yiu(ZG!8W*XLPnH+1t@<8&0N<&9LQ#vD0Rs7`Gg5z1-d^ zAO_u+x?EH<=k5AV<5CnRr{*QpVmJ^tLFHp>&>26PoBc!NP;8=G-F#i&Fx#6Ot0AFyXkYQmLw{ zxfOzIBV^WsC6)tmTVeHc>+Z}}m0y0(Wpf0M)CWLeOK;3)gDA>r5hbsjj8FTMGMufDrUry_QVpR@$=PTBu`)gGh~r`Ek}68X+q+_+TiK1rLgaQ5_4dg7J&EulBl|W_J(WIxux7bVEIZT!PM<+a{1< ztW_$YMyynzOrJ2K%X8H&8g|C7<1p+BB&4VlHC(a2CRSK5gx+lOE=u}J0>ti%l~=-k zjWg*;-r*MU#55!u|jj5=O`Uo`}c12X?s`#MLrV*!`PgJZt@RtyisJxjIIF>2u0 zKnG*T{4y7>6C;>tLgP(i4N4&}02YvZa0Y$k-!b&42o~x%;+Tc3sO&J{4=PCfrB7l7 zCoy;ffdMLK?FXKnF;8&$c<$A~BGH!bMH$sL_z zMKNuN`vz{@&Hv6qBHkKQuxE)mRCFV5+p#>ys2O6ZuLaJa0tS}}->FRckJqH6R$u52 z8!>S^bT$o+VCfSp5GNN}W;sZ&T*p-DzZNpmimNim<$8 zQ4~#WNWp_ygfQNaTCutJxDaYe^g6DlT{sn&sx zL`>Tt1|@wH1p7-v#bo)*-v$E6#_RKMMRC43 z*(1Q*CRn1X(8KCUv<+IgN@#QyB!e}pCaX?7%&7UmFV;M4E<6rvDY=GPZh~*qtZTeg z_=+DQ1&@L0XMMjG>!p74m&L`@_sbLFbD}|CtY7B%V4}vSn&H@NIyPz(O3X99ECsod z40dO;vB~)XiZmk#F)wR=%D?xJVW5>LJIYY52R4vc<Yt2(mBInmrElY-u852RWD$Gr_TH zUcS~nuFi8+m-_g-BS>{-&2Q!Sydc>KCN?nJ;g%z<66EU?M;LHS*y)XeYN!>IaK{mBCSKfv`9#yU-+xXSkraCs4lSz)^oS z#FBjAUK!>&r45whc*7r8-g@nGU5#!)Xf{PY(z||5Q#8_R&Rci$<|GmXzVk_Q4~88Cr`Li*WK}d{sJ5xApT%?#9|9A7_+1HlT!Ez>Q!Phl zs-a{>WEfAH#MTZs6yZ7*@ub5KKNE~|e6#GQJRGiq!!c23=_4cWt*AzL?iueY=bS_H z5CwwrwN0)C3;8?4JP$Vf_ih!JSeREF5b$#4rA=h1^^fLB|Kxan|)&iq?S; z?j&v$)Vl@UO0mGExXS89d#vFsI!QH%_uQirC3r+I-doNzw9AxEvQGru)4kC{Oue(s0uI^dW)08swdZc^bj zfP)kR8G@75(Hi@!#-3KL7<`HinXu9V(`}!fI$+po!^u{Ja`oM4M`Y*>gg3O;Y}7dh z(Qm|em#PF@&*QO`@4Tx0cx;$83C1c<3}Dyh!H|ui7yk^e%%{xfumX@&fGz_OV?oV? z0>B`S^1C%%8#2!5$bOMub%{A{OJSU=6uKF}++}ePiK%@j;%jMVYrv+kTt0}d88zJa z&n+1DO8Tcc!0NmX7En+|1|J!9fY`1FL}3q5)d+X5-!C4w{cQg zC9)YWXxg0mAton`{9Zm%LB{GD{R*#M^3 zYB@T-%`6hHu61mNqD>xeVBw7VqXyHlAsR8?9la*m0W&w;6_Yq2MT~kT+dA_V&)6_$ z%eU86ZWw#Si;snfcZ(RR{W_Pn|%zpR6SV^^0-!RT2wgNNdTG8#q{70OdbsX8v$m(e`d z>^G*U6|JiAkTZmebrGTOO%KztO(;Q;)`4{F{3e)LJ_?R+>Wso&VqvUe(R3|yNAD8} zYbT(e|4X_WD4cl*K9;9zMJbL01uj`GB}a!nxlBZ)&dG^IQ%?WYaY8WjQmwud((DQ0 z4c}Boyq=RIE>7xW^V@wBvK922HjbC`nDSiJ#7d{Y&sjvy@IwqyQP2BUfn%To2;8Dz zElely3-gaG`5xembr-t`1@C3Qyek0m4c8{b{K2*6tNo^7Y&#wfYb=Xv+}5fcxH2^7 ziIJHwYle7eiUae=OASB!vXzSLkIR+QaDp98)c#ao@)JG5nsK~W{QpuoRQZ?SGPHjh z6$OZx7mu-843;OcJVok%2?ge)a^MDx78Lwdqfb$> zArNIwvLSbU#lSQ7&icn6U+8J}NRh~#qy)W{Y~ za*LhxF|==H?cf@1<^xrc?FsU|Qnh1>rCDu);GHQkw|Br_~h%C^QFv7AlO3Y=k~@nf1E>(n2(@wA#g+sIib z_NYH(p6g+d!JQhQn}ZY0?zWjFS#3HcoR&!`zYOxi&CqCjqo!0hvl>#Z{y z)-~EQ6@_iVQB;@Uv>LM?BgH3zFFVXtyI1SqMc6d$hqm-UvGitjw+JB&ziz!iQ73;bo{T2 zdsfN{->~e*ZYUfUY7{DgnZd-x^m!^_SegL+NUyAHGKGmQW8QV0nqH|p?$EfnP48IQ zx-=136DS?yCFlGmT&l@NwTrD%29Y2piuthI zl<1g5h>ZqOz0z5aMUyMk04M0P!vzC7&yC`TwQf_*X~$M!eX%D{apk&>NW%asst6-1 z&iJ&M?sTYJ2k=F^sWA(Bbq2zi$}^6%`G{h5KnFq-;NQ^MBy=py)%&L6E~6 za)=vVTAfM|RR13sI@8;P8@NukPViVJMx2I>jR~x4rU`%yJ5l$$7R$mHd5AU*@p@@EBf+> zQ&G6U{U$-d@^$FofIyF=Z-j+pTfziWnV*Ni8v#O!;dndHQyjvHk$Au#V9BSq_krT~ zK!Tn!*2*`g*5sNxRvOqA4j+g&G!8d-F913Rf`O}uwxcZNgLXBQW?@+%7Fr57mtQo| z{)86q(6`kt{kL7awmGR6q(VlR<&+8>8X2E9FWLFPNog;P4uVMe2inb-#Sv8-GugZ& zbt6SHPsq(4J7$IxnY0vTTXfcF>O_@wax_8ZsQ`AYGJy5cA7bYL*k{vhlwUG}UIJ8` zN^9=5N;+Np7H9&m_o_D6h9K%WL8@fk}(GzXB?O+ z`x5c&fi}O!j9~biteUtok7P@x!UOXOtyxIVl&dGfXwdQOrlvW$3HS;;`NuB#KSL)S z9xU3na+(ELE>F+01zl=@3$t?Bdhyp6nSV`3L4~wU7O&tKb7wkYFWAx`T~l-pTz+i^ z*kz@{)&gg`8Z|P(01s^l$Hu||3x=$dg=jpTbe3HO97U!Q79`|;-zY|fUkHG%8m0rZ z$5bcSz+HwbtyUn;#L&|>RffZ!rfP`pRHz8A!kT7N2-b(dWZCs15ikZlS*KD5X7IV_ z2>5*;)F}vc^gKEd+Rjw?!C>%6-T^uXmNBr5u(h$l@_tth^g zcV?uTpmkL(V1NBPO>|}6(DB&8zT(VGKyhk1eh5L8`UE>hw!B|;ff)-jwQKZWyL8e? zD@H1^B)~zoS^~a)BeRzrU?Bn^f&c*o2tt6&p?eB$f6$Z%ZXRllw~{2yC>KDG0Ld(o z<2&p#ZP}h(t+8CBfC3STgn2(10SP-MbzKfw*X+ZHxn`A!?WyJ>0=)@o!lizI3R?aF zJ!H7S$;iaIwd-`&A|7eP7f4mB$`XxaAo5<%j$K!j8pb?4%U-8-B06xS9a`Wa%7a2S?|CE*`^k{SDZbPKYJQhD(H< z>=7(4zhh-T69g|gvg;VN6|-W; z0tSLvAwXsDQk6N1lua{Gj)p`EYBb1BxdBuKY$rp+r)(qC4&x1(pdiiq6gpn;D*FAs zmHd(%3N0rd4!I6#Xc3S+SzT zY*Q@jxyfFCLT1aU;8+}E?)^?wb$uMQsv!k6#u1DQ;k^faM!ldV8WK2MS*&=;=88`J zE;`EA#~l6{R{{*rD&xobWGMqEvKWj%i(B)4Wp6MMAL*a#AW{zN>-&uAjB?Ba#crXL zmzanv>i4_qZzdIuKio@)_>cz>!(hh0yzO-+7N?-Lo?^4yB$0=>4j_NyOW-wa@lLCMbMk`?j(~Yw^v_{EULe1z!rPRWR;m6PQWu$7&#^= zcMX{HiXs@V#z1G zI^)z4+J9zN@Mt zWZEr{5We7I2*sJHLeMF{;}_CpLYHEtS_RRY_+Y0UD^wCeCZdk(Ay}b!d%3+VEjd&$ zS!ayA@`N~h*-JQwhn{$xuW<&B>UJxQI+`2MLDJ0ix9LfluzpgIrQm-FHH!^ zggnB_Xg-Y>0CrIAfC4-O&4M;E_i3eS|Y9K>|$7=Vt_Bz z$vP2^cV;52qW8yYQHLy8vf^feccH*sXFg++e2GO3yk>A%!ByO()LA&Zp$!{66B2C1R*y1Obps#qW#rW(@`Y>SaQJ&G6mlJ%>TPb`385G?s^ zL)6M(I7pqXNRCf1)`e;4qz}+e+yE58>_l*#>pFv)Frmd5^9%gg0TO-3O0gD80;ugC}QCq{j}=T?7>;?5z72$UB6#Z$63jC=PvSaI4jQA z3leKG>A0AVAjiVloF$FCy+OGw#A$}En~Ck~$Zj9#yT+a2Sm`ipD za>Wa>=S;54JX_W7fv)MAT|^V!mCP7E%b#SE15l*csJ!Ve^D+ov-49hgYj#=eDgoFN zwnlhGT{4!803aulAakGwE!}ls@s1nsxnXGP)NX+|G(0wAZgaOorY1d*jUb9*Yf{-y zIlyv55Rj99(d?35t{U89$)Gzg>WDE9CO_`qQbefqrFPM}p&D7Vd@Et}O+4isDPeAzRW5_9P)CA?)|fZOu$PrBOk{$Ra4+2D8q!sP{@-8 zi8#$;vsU4bgM3y{^UkBKS76&+M@vh1rkq=dbDs^4Kg}6V9GEpLwZNPNS3fO$p*uy+ z1s2H>f5oxc)Xv?N1%W>bL$;&!b{tpGPWq<4(j1k8aEA~wSsu^6D+%!CGo~sqo2;66 z9ohtAy2@7{@104{V#jXCZ372%(xb9s_=ii^=_pvGy6uV2p>`k;d1_eSUlGlvcEWdg1QibW+)KZHeQz@su z8LTE3g8Si)?P?Pn8rez5U|YU=ohuXQhaAJ|J9vMM1?*HulW*4zdOjb}t0Rnf%4!Lt z*Ns2Bs+r%meyLFS`l=En=uSH6ckx(bLNGZAqpLQcE9(brrDg6^54PRI65T4hsb+)F zWLxx~3{dJnIba*G4JZLfB#^bJe}>rZfPtQYq@Cpn0=4t4_UzkYzh*9F=?6(MiFynO zfVyV}X4PpbzIa7g1nxGdEzJ}kDH`?T(lCQJ{@Ef`w=i`p8L-vRT2|WZ_>>&yL!qm6 z8LM8JT!DQ+$uF_1k6>%{tUs#Q}iPI4(%)ZxD>`=yvFRxmuFOF>UjRs45lx#4c8c*?41#j9Ht zoG@E6Qq^gFzYvs(bL>mW56tRN_UTvD48wpLB@|OxzdT_^{dPn>V%k+6utZ9=dRrB$ zm9ubEiO58FiD;jjZWk(h3 zrOtV#JQIj7YLacji&;E_SwaXtW-EhOpPSAg zjPUHTGx%tBwxZNQ52-aHN& zPMrrkAJ!+DkY^wgBngNh3U7dgA!HWQg4m^@RU6qnF1=Av36fO@MoI*1c`1#yptk+x zEDC8w%$S5+dpUwwTSFlN){$McXqv7x1HO$d*K*7naJ}^+H-FhO$%KZ?=%X2Rsl3|l z`3W8UvkZ?b%vZ#~aDQvDQC)oC81ZzL!81LY7Tll(ndioU-w4T+$!R#6`<`MZwOOI!BRW3%J zFEHd|at@8O+9XG3A450Q9AQ?hcTOh;%!2Z-#uY)%gFFPm0Iy}pIF`*DIA4;^O!0sm zSshC$s@c$}$237(F-qHrY$J{~eTBEQcJM^|@<+it4YO*58Pfs3GAH5#QC-JKsnGV0 zv9w=02J_Bw2q$Y0Qw|ff{G;Oyj@0*_ygNsXj?B$TcC~6mep;a-0^*e?==-88)bHA_ zV26M!I}uG65i9jtU@?X`P9w%}a4x7*ZmxvUAvS!ri>vX<=q?H{=lOAA zXdNE|g^zWq>)|2x>5~7b&d6c?Ze0B9OzYVy|Bx3OS%Ut@Faj##&ywbCT^2=zsjK=hts&?SJIJmV5N( zIUyh>2#IEcp)4y+P3qg|<^*GpsSqE!o7rJ>_3NPlTsZD}P*N|$H+i24Iat#pqTOB1 zCgm^IZNVx}3fK)@tP|9Bjz?oGRWB301j>G6JQ%jUwGJI6Y6sx4T3i%HdmXa%l-cdQ zqLnp##s(N?;b(S2#X!seG~|7i7dq=u%-%%drdkCCMTfe;$T8^gdJR}Vb(pCGLcA`l zd>GoS_=%4I3C{=z#%7-gF~V0I4+1R(;H2sewwByDO+TvVtzND|c2*A2D6j>Sf}AT( z1DLwQhiM$65vgRUleIi;1LZ7U*Otb(@F0ej*{NiV*Vm+&??&tMl7-6%vJtc;X0WkD zUQA)2lWur*te+LySY3k4pL0uWQO#- zBULFCw}^`f!%6K_Mt}Aj*tkb*+ z%L);rkNiNlDn!H>C}cwRZ8C?SiI_4Ya9}e_Y#>HmxPf-1N5%6~+zIgc_3aO#y{|?M zh1eN1vkdxK7K;?y$X$q17AH_7XudgRNZ7sA5fK8YZ&wwMO%Z{V08#>?BoY9C2nh&8 z*-k_PyB;|GuIo2GdjH_=hfYtsLCg^>t(~A=guDH_rF~6FJYJ$dB#dA-k_L}Zux<^(@(GpiMO!cE^ zM`-?~(9&>P87msVGInfZB%nNWXH@JNvC?e%ifq=3GjLxZbuvESsmCVd2!bVM5SUc0 zk*cA2HxtRo*|C;BtB3&;XZOfw-vzwBbFq0V+6HCfh`KuV;S_>s+z8fJ-HJt5J>7mU z)kLal8dMSM0t!G{ZFKyx^C2iv2VFy)Qh5Ekl~_Z*qP@rr957%HQ|V+>|9$2-46D>4 zx38de3Rd8x3`}tVirc9<56s-~UAvJkVxxeN%zcMm<^A<@}ZZ*oOABIUk>|i&Ov!4fdWEc0g@E}MMRK@0z{O4eiF}qL}QWsvk?wpV}meQ z0u0!I2o@4SLSRq=p|q>8(&k-l-u?0o=bY(!e{`y@uI`z0zvZ=7_nR}*T~%FOd1iWi z{L3x@bMfpT*@1m`9Q zn7b%jzR;!8l~)yST^KYT#AiiRK75Qhs&qkx_TuUw@O%g{^Ml23k*Mgvwd5H?-9hb{ zvcwW*p}WH=N1dganld<9Y*+CTwNNbc@>$9UsR}UIirk(Pyqn=%zakHzDocJ`JSrW_ zNJ_@Hfw+njtzIoxzX)*K=yKEp)N*-lL_N9KucUor+hAd+(L`Ax7t|~tCQ=ZsN~s9O zw6v=H8LdYn;~Euq4u+s*A)uh7Ax^r?5RrInrxqU-(!u}$AnK1k)e=9~#-#MM5b@D! zWi;MY+{UvK8O$-HwpY9`fRYbt6{%K48&mL7PnM}Z2iUBou8@?qwPsAUVvvkMYB(k1 zi-{|fAs&pV0L*J`nPYL+%L!?DTYC|bnABuo$PHo1G@-C7aj7(CA}MPxVr{m(2HGy~ zX2gW)U~xx>(Zh{JF&Wk5V&%M#Xan33WR7Rx#0Mb-0Z!g{GF#NrL_Wul<)s8)tCo!+ zv}Tf8<-kf)fz-$tLvSNr8B-)Gaq$wk0N&uh80M6emqU0Ai0s&1){*APpFk}-tEKKL zg{~W9D*+*T%4o~8F@L6R%1dsuTE?(U@)*YriBrnx5yrcO9I`BA%!L>w0e}NQ04Sv~ zsVc*WF}#ge^X1E`MebxEGNaS;gn;CGqLfEK?DPU*3s_DLtq^a1LVIFXyc@fd#4Ir*$17) z3=8RMQ)HC&ICY;#u>Bg=7kEiNIF+5 zkrSnoCQJVFb!C3B6o4_YT^ZjO!>ih-4&n7=00>(!WaEm1fv6yM9`Y|3{>G@tt6 z<|dKwunyBxQIZK|qx>Nq4p(&nf~u$;6dofTXNn~1M*7+y5;VSvlf;}&*+C^yo%r}B zlp3@E7{Zx&Z*mNxz~rhFC5(!7Rm4DD_X1~TY?J4f0TNg9%+-Xyf-Nyx$c9JJEr|^= z09RzW=#7Qu6Kpy!<&1KJEGh&sUd;0ogq0YH3jlL0v&`_R$Jg;z}XooC2^ zwMm~M*V8l+fZPU{l}(yi%QN!~RzoS7HAtaGQ(hh&!x#}!@jOdSX-3<2-5Fy3EPY#> zR3llRJC?!R`A%aSjU)>WdsBY6AWN=9}(h=;6G==?iMlGR|R@& z8x%EW<<1mW@L`9pl6)#axd)3soYGAgzxQ=_>2u#3n z*=bqjDQHMAlNU+HK0Fo`mQ|*S0&GcGg~P*@f`X)wHH@V&^Whp-87WDA#6=RXBgF#5 zXtD#8p1R1I3PBn|f=eu|oiN>wom!V(J(>$a|(;O^1~QMOZ= zuN9&GaWf`KMx2@`kVKZP(#8-!-oiJ9mMX`R?F9tYXp8gmxC{fD^u*59w6rEs!UzvG zy=U}KOk4cD!;$11pjkcQ0i=&!XMJFt;wQ-WX=Q|1eo+f_>;kDanQ)F4HEIb0p zJNU&E#7)$qBq@96WXnDc?l%B7;0)HAN+}d|J15g68G|vOX3eWEp%lu&!ca+eX+EIN zB0+sBQszb7ObNAFj3KXO6gUl#87eFe6q{aFI!Yo-)WWRh>x;p$$KTo@Mp%NMbRn!O z`hGm_32A=3xmbq9QtBd>@)4wRnsFV8z`r=;VU#Iqiuwz|c7y%cuzd*{SZu=GpWV6t zwtj7D&cAYc;Fww8kJAm)O#@K>I#pr=LyBx0_CY5tQXR$+i{PF%vH))Hosin20WjcR z0Rx}i)f#zHUEB|hV78ZAx5mrCcD6lPpwo~_!dF=b&_qczq&!ca`Y0GE9d81E}` z3(aLI4ic-Gdvy<%2?hl+rNFnNYE^SfLDGg}SkPq0;*zpHq9~JUZ7T_Fg)qM|4IoW-D+HL*#2sJMfHCWO3P!A5-OE(vi`S7CfwUeX4+Oz7PxS@ZM!<`s44`PExPG4PT=~UXDTUPWF{KKyi23RgF0^gN~bUMGVi>bHkfM* z!A7lmyBZt-jNwtxL(GAUlv063q)UPTs0i(9xFW|hOnMwrv02$A@_!V{ONP5P5zzTn zDsEgPV2yAI-M*qSpbUSdu8#D+P^|q*tv3I2ff-wS_>cq6$QU%T4WX>fPVF3zN6p=> zxPV^}rsfcB_>#i9{@}Q7B?3L-D1|$ZA_aZbRm%faJrrt?7oYh+0Akx1lF*kTcBK5V zigOd-97R>Lv#>E+Tp|GW`VJz(iF@rGH_Uf7`-`ufo_FPR&p`wehFb8!q1)$}g5k>S znX59fcfkPsUd!Uy#f-E-KLvA0{!twu@|*JTi6CHyfPjWf+(wx*Ho*aKB@cByDF6<$ z@F9dNb|V=BU|?+@tnKUeAM1`@GP(a&d;cAF^ThJ9$C&*WchBv6==nPLj#{iy0_8b47xU!zmK+3uTa6SP)Us2rL= zoepy#G_*j&p*d%Jd6CP&pm(Stip&ua`N)-x5pw`l>lo}$VYXNF=^}HZpvA4b@zuHT zqKoRUL%0o^a5VfTOPM?#ie@tm9g|>}SHgJZR(9xw{4hGN{hi{X5P zB%?w+R*nU!6(T9#CU1BDr^4Q%9JE4i3VFX{st&`M-#?-9w1 z{&rh9rr?OHs6tsV(j_Y(!3jp+B!Y8pwjywmCuM{+rZYO2k)zHXhysTll(X8kzGJba z$?l*Hm}QneWI0t~Q+b%6!5I!4YYRHzhF>9+osjWaPC(X+YN6+F*`m>e7inD839gr} zM_RGLaw8|EVcZ)?2W75ZK@=H?T7xXFf~Jroi$su{nX*U8H*UPdLhw>c&9LfZM48lP zs?war+{8ATDS$v@)nbXWz?4y+hJ}A+T<8BqaJ7L{%IEr-CPnPs6?chI?xvO*L{42k z2X|AiwX``;vUQ2&j6gbeNIl+vOMl0W^YuOW*k`UExWHhCmWZ-_f)Ox2kK`*tneFz4 zE;qG6NyK9ye5;H%Or0QnZ_Bwx17x{vrHGdZ3;+;WieKm=WQ_D3<7UymaubR!ZjCJW zK)(FqR?t_6DDVoYo5H~hyS<0Z!*|g=H{07jxx9Qyhc4-Q1nj{*HRVwROB$&5OPijk ztfqf#iCS8$2K1ozR}oe8D=k=2Hi`=2jHCiz%?!r}e^|fM9$hWdn@na(kqU{QbUl@n zhM>4Vra5oLh6T%~n=huIiGi$7cD3QoIG5sZW`r}OgI7H$j1sqSlsE!{dahn_s+^$7gDN38KNL8e;DU?{;K(9Kz%Iifj=2oIY9d|3BBmf@o+oL= zbW$)9i#s)yi6`5kU?hYp0$HIj?McEQOBf*l8M{fMkYP3ctaUEo2$!*nW2zyd$ac}K zrUii#aLg&qX`2i|Lz&O3!y7rpNOX<-qEsc`t#+nXIA|w&s3q8g6t+5#%pyhT>z_1d z`_&b}QcV)-HbD?o^9d0w3NwiE*}d%9A2FO64XW6dQM^%?IG`BIfd=q$l)xwiDR!<= zwLAOVpt+$$%@CpjM@a{jaI_5hvc4`J3lhX6Nf)Xp$dfA0qT!fQoJwV_Z(W6UgIXH} zc|h5W$cHI>wAvW3BjbkJkQnrch=)&>LkJ#S>TRZw#nNUSVp_2JT-3IbeFt5a>cGpf z0*;njR*?^)@E@7rAI}m4bCbMGB13=b>@&rauVQ?4TZ*mijI~4L79R-%eQ9WY}3Vz5)D1O|} zOn|J*ywP_gzpln|J_CaoQamc|y<_|X(1pR?-o_g-saE7lK@_t0$vRwcE$-Rh-SJ7d z?FM^A4@a)-dW685$ZhVy2|!l;iE74sjM|CyAFDnium^}C>}b1QZl+XgU&|y*T%ubA zS_q79LG4@KIWt}Q4Kd$9G9lLY$~{@&m47ihtcx5imbw`N}(r%4d<> zNu{V%D}p%d6iiTSI->)Jv8|#t06;ZY70xI*L#<+^P6`7XpOjuO|fKjoRWO%R9_^T~R#g6HG4ofAV${&7Sd%^{B!&_5E?zJNF{2_liB(fPGagV8FBf=pjs2RaIgaky* z9HPQPl3RtB5_jdUa$Uf_Nn{$x5=rzZ)gGbyDPP_#)aUDdopt=v$bj;9B_cA$8sndwdn#cT2=(rHmdD3BtD z+2}0d_6A#GH#IvDRJFCHU6U$XnNZe1acHZ#;>Yc+O{3%*ha>?B_$2JwhQuViXl)`S;QowtzM4`<0yzo2}-3v9^@D>2E721vT?OGHV_j67LNls;q1aVg7(cnUtiOPC)|jZX>i2LodIpFRT2f zuSF3O;ly+g-?g~wliLT+n_T_W^|gJ}F8~3+KwbzDtoE?I^>KzN$bnC5$j`>0`@~cL zN7MtM<>Vs&Sd@j_$5Q~6Qi)iz>WSDA5kepG8v;NeLkLJFn4tKMRa^nxcoINGvJj{T zG_>~^Uiswib04;MecEp9H*1H{E`xkQk`W>5(6lgG6j;_l;!gp)8qlccD+HwL>Zsa4 z0j6=XVZ22tgPV?UELKZeor(gj)Uf$85|d$!J!{O)k5{0t7Oj?cH3~+%pc6Zf2^z+P ziK25LW0V_Jvxpka$#%oa>N}mJenWzQL zs8xl-JM3~-juA~~btsFY=A)DL)wmR*^j#68rL3Z;Q7h=w%3&pKHSCWTpGV5u$dzqp zaOc?C3=w`*?aXJxD)A~AYFu7}+14T~?wP3ACBq>_Lpq&d*ccwFsAEJA zg_!A1BB~IJR*30UhNThkYJ`d_%5ka>R2NG`vEa=;S6FvzHPw5BGml5{Ned4-?Cw$$ zk<#A~Rrx5}lO2jCz`lExYS)m12^6{Ka?&{$g5abxTPNtQ&(0?^bLErvtRKXFPR>*z zMjD91+yXJ=bj3kK#&4qVh)Q5~q5|C~$-x34fVEciYTf{2K%Bp64u3Fkf`HstvJk-S zz+(xaGnRmmjORHKq{nm&M2h3vQW1G_q(zxLR=2`ezJPiFguUm(rB9sP`q9OGpY1Qd zegbPiL^g;?%7yZ&h)AdwBl4N^@S5pSltvCA!`U!9Wl@0t8tPorIS(h25jYxWk@+bV z1)qM+O)w3{v}d-Q3+Ew53?=+}6Uv^T8IzriW-LxH=8aBR^F*|aKFcX&HoUZ-ns*TQ zFO|kRn9@g;2CPIh=yWh__=1Io)hI7L8b-6jEFY8VNHC|I(?*EKjW;H4`Tl4`y~)xl zrnXj%P<%{+Cl*>>iWjw@BDr*L2Q}{<1YWK}MflZl)C)#od>mm?sreLKHMUWm)Mx-DgGB7G%71bQ9e=l4Mbuf$?4v+wV$Z`n5h7-2{E-@?VAH%4Se2QfieQS)10u$f* z2ezQq;Wc`qIF+-AVSIVMpsLn{OnM%2mm#Pcb4i^@Nud~6iz*>Vd!xP}l5ox4x2O`G zYhJ8OA{vRb%K36$CHBEKK95l8$)og9DOH2Kpe#HpR^*{(rp8UY1Q@Y!OP(1tj&R87 z6OmljXh2l{lq%V76s=QzyU%>TRT@{gM*J!o=1HD9mPl+!GC;$SA|2{-yfXgq2p%DG z2@ynk2`i9+X*S5rfw$Z-oXYJzus)zKQ^;LflMEBoU*!yh5D*ZdF#tV0aOZsUbbrO; z*AJX;Y;T=W=G?QNb4ExPMSVcqtu+}b@3!<>?7y*E#05_Ug zzb5gGt-w$K0J7G`pp=M!Ecr#Uro+jkn_zrv42%Ud)<1mY!Nt%z`2xn)$TrF<@o83( z*#t)}#WTmv{kQge4%_pu#bpoJdB|}c$nS043e>o2=e7lzbd*4<`cW|sdej0 zLTa&Km^mUd0mUnMlFGVAS($2v{$)qa0uRZ5BP1Q~t!k7(AgJ9kBj1FlrDTzrnU~|X zHwRw|s;a6uLWXtkl%q>jsQ??fZlmR?5wJ;}Q*>HOlSZ`Yt_On2M#@@@Rn#F9007YH z{smPT{`ytdLrP9F?yFvEXL=QCH3Xv!vST|j;je1_n1aYi@&8 zW00zbU0f=W?ru5*Eidkz8zQV6r`DDqXv$2*oKkKy%H>c{ls2U1b1{F4b4)tCYO9sH z+Wl*7$mx@qe(n%~SuHUzGGC#SGbM|eh`sBZ3OywZw!JDsst^hq>M2FDg(MMSQp!+S z2+XhgoA9bv3KWiAkH#AFB`IaG0e`T#%VoC&SgR$eh=jIO5-N{eu%!%BO4NMfo7#38D(>^<=}F8nnH5Ar!ipscDuwBvkX5lDox&SZ>^td#0|5Ej31%~w$AD)MIRX$j zI|KmGb$IF#dhnip-@(awmrk%F==s}HuDBCO4M+_FfJsZ|z8;lcYr@c%WspE%fWY=k zn$4z@NmuggQEL{?oH=XIn9cxzAwV!h2yWLM6Q<#WQx*ACx{2(<$ki}C1OQr+nZmIv zx(7dRKYv?)=)!KY$5?B-&K0D5p~}^spz>wS_*2vLbdKwEu1d(^6;cy#fIyM&38P2c zmWbiC)-1{>?lC?jQZtF1PVckAci;IaYgNND9HtjT^oj5*g{6_qVVq1HV8MgU2gVqmMPzag5T5X2@{fgd<$fxYKnioJindVpoUU|69G9jh z@;DOD1&^IiGlEQ(GRBY2A1L6`&01?@A;4;s6)pjU?R9H#xIh&_FVr+nr8LXffh|%s zxhlIwvd13!1Mb7jmUXSn$YOLE{WNt2$5-l=a73MuNJgMT`2nm=U83+ zFdeE%TA75G$NXe%$_=?K)11;T3q`9z7@4>Y(l)rVZ894482<2Wu60};ps?*DrX#|z z>PrtQw4$5nl1StHBO*9s_bgQ8xPcsZ_pCDM7X__&D3~cxf@_>mrB{r>|qwf zM#~`Lt6>Tpx$1H5SIM98|L0zc2>|iaF-*~j5)x9xNR#A}gU3vuwVR><86DaRkR&~C zs)jeL$!PbiH_H80S?n0;zg5|7gqXF4S`o1%tURNls0nw&F$~gNaY%}Xj0jTau<+xH zP~%(4uF_ydl^u_}bTx5CWqg`<3|2M+l|9E7<8S~~C)isRg$n>+UyLH)-}FWV0xb0z zypALIf=m|Y)+SO?70AwX*@xT!gz1ys9U?99#JzU0Lzi4TS>KOj0SyuYSQ3+RFISO! z_arYqrK$OU!tTPxUqb-@LIlBVHaU6f%sb!puG?!0%Eo%wt|pU);UX9*3s$!B{T zWtQEqO$-GaQq4+u0qUE>D%lxf@bSz~`=uRWXIC z70PI@IfY$Omex~vsCxLA4qY4~JPwU|Y9{3dd4XoIMU(4dpghlS-hIuagn?qmcB`Hs~!uSA+7xnn+=pLaA*wpM7R$;7`_RB6M*}0jDG*qXYr-B3V zt$L}E_mW!et1zL_vn{u3DQcxQDs>O#t1*SS2BMY76U)&q6S!3uee$%wg_>0Z9WEG| zaIEgdxQLF=Y4o`_3Oc(bZ`9b8z>X;)jR9EeR-Ug1BxRbL3rw2;Zbl*wCn_vCh>@{C z$nsA}y-So8PD}!{WZ)nfYvhv&?4a$H|qjptp?n-;~dCJKBm>j7!0%H@{#o{*foZ40xKZ$+6dP?UV~4N!%>k5mPu}?}Z+Xl6-uHn=9)3Vwl3+nGd_LXy^A0G* zO`3@n>9h75zxf+4ec8)j_qy-D@WKl=&up8?)N>M{w0Xjd*Tx&g77JH z=m{nr9J$aua2FiEzdv^Q6goqezyue;imFkpq86@H3fC@X+c%VOxDb_<@%%UvD;1yR zoJ!=8U*-B*Zjr+$7}r9EWfjPowJ2?@i13Prh{`O=R*Ru2+#%q71@=mbpNIrwodG7u z1SEA}sfY~bij~D^WeZ!bOiHXsIbR;65D`F{lrb@*Q{eEaiLopK@#IDOD1r_)1ywjR zAMNhfxGv;vLLB91p)r~9q#`l3su-VkqJ9@qwY3nYP)kTKd{T|ARO*F!g^}}3Kw{cd z&aSF2LSRIfBGjo4XNV|nM-_~2T>UB;THwJQtuDqnC|=2xLaPnu9Hj7BuEbClJeN}rR>1W)Fv(CoyuvWvOoYV?B5uop>rEM5P zi~^vGJr(y&a4~RIO|KDL z0F04F6-emG4J;wb!e1uj<&q#~DHs&bydXeP3*wF;Iac?#GGl^RGa`PH6ElH`KlCZ1 z!)n8#O9p9d6Go>rb2zRog+T<$tfxAKv6SUMJykKgDhF_)AyjLeZX!Zfw;=$R`D@xkOk-I@m#JDb z2A-<=MELXr<0vUEw+```68WM_sOYdf836Ij$$op=UVK@%zTc3|qjPTqZ6H|C=cbvT z#ZZQEe6!T|7LbVguIuLW<$r(cTYu(fe&((_@3`!W%f9GYUp1Rfdux}A<@(0j=Gn8K zxZ#uc-E)7}b$}KCi3|V&7(_(uz;qBSurbE=%g(?Pp7^+{uex%3YX=QXr_+s%^*ipo z<8S`@Z*REq#?RmV`Jed7H(hw)h0C2qH<8^#&JgQ3`K$)^*O>;8m-ZD*CA)i?}k}3Mg<1$OEX-d&q zCma!p)SyVwkwOOI`SwO#?WP22?NG3mG>&O?ArvHKR!bYCnb{Ha)Uy3?vq*~)7BmZ1 zg9FMcP>zea07OUD+AgzUiU%8oo!3jG@uzCgb{RLeC|SVOCn6W(RMo1UwZM`8WN;D~2u0bNewKuleAu1cX>hfy zr%Dv5oLE&A(V@gBuuBxpfEBS2ub5%FU$s%Yu064G4T1K6Pta8VeR8)e3|C!8vfuYc zCYkIguoM7!JO^R3+?dt^B#jM70U*|lIEmSJDI%I!Z}xZ?^Dy1p%_fQQ(Lujru9q=D zJ)J&of$aXH9d>A!Vva0aMmL&ASP0V!6J)cGG8X`r#k_u?OydaBY1L*##nyB}+hH2n<1I zumc2xfCkNEYH4e2z5BYa|N3wH=5Jao7F{y=kt z_2d8QNBiZXw+lox&;j_Z>%)qYBQ;Ez67^{j03dBTU=ZAop<@lNFCu3#QgRxje}2h$Qh|+w$PnG1&$b z$a+$Q9O0BK$}Js=@&V#u0$bFt}@Rn);+zS&iQbEHU#BOGRkeY$#a+#h!$p z#Lp|$@JeRqx)_sTW$vRyAzSF2M1?(K$qClC9ta1iKW7*ECv>ujGdXD_z!HUEjtG*3 z5OEQT-e$a4;i4RIM49G06bLy{-M6S^aCViUkqXEADat3# z=AwpJ!u~{+&ruBQOfiN+P75R!##A;g8%mgdOi~lc9Ir$a4MnbWI!jgAt4+4yaHHh#d|5T8A_&aqGE9b=hL@1b<*4P$ z!d?|=Xtc)savIdueO>#ATzHz9cp?zx=@hm@h~qQw&3e|*hXl}*g8pPS$?%7r^H1vp z>ZRjZxn;qma(;}FLajDAjJdfyjl`prlD2(mYrgv>Xs*p5dT~KQxe6*z#z>@UH*3Rd zXH?td%s`Aa63lVyrPHh(@Nfo7?w}B9*`i1)p{_HdYLKD<;X)G&B0RbyF&+bRzH)AJ9#Z%nm&Dl<|1d``Os*TBJ0B=drG+gi%O{55-fhAT`yDuYhGuKH zwztbhqgc0qlutti?%ut^7}%oeu(vduuAM$}<`4hi5AVL~^I!iB-|#a({WDizb9I72 zgoyKh?`1E4$@SMiX=`(8 zuu#5a18~YF_+KuB`ZF4~ab_u&^Yj=YK$cJAM~qAwMFhcRKD!xITff{~SDA*zm(*9X zU2#aLVMRxv59b+N#UNxet!o4ZJaK7CzEztV#O}iTFj7};je=`EPk?j7&B%>-H#-t`61~#e(vHO28I5&}#8&os1Xi z)(9JQM+T5n5~~O9IHoZXR2jUOhbc|4vlfu>Q3R}>y^W0Vo-94w71MH8OEk7<1tnh?d8{^JBFUKbXUp+kO}a`(^E z7^ZnhDf4pqg|iw(u{hb0RELVn$Kp*)cEMfohoSPori)0HVi6h^JFv5DH#W@Lh6%kh zRz=E_8KmqyWKaDO5g-9{ew~d$YmI9+e(F)ZW`+p>;vOs^zGIarWZJ6!N9(q7POGHSBXdL4*%PfNO_AQ9)p&Q6M*0eQRiTJYw-Y|pt5~KqRQ)Y8f za+MNVL}iS!^_>{OfJE`~j0#VqK=gN5d?YKj?3)_Zl1f6&=`T5h#eJ}dkh0rW8NfIl z=biYd$lK_?0m#{+B8lQ;K)hidv6mC!G?$Km3zCp7?rT74nOq5vjPmvmF&0I)C7dj0 zLK?#IE8){4K-B}p;{-Sir#MY4*xC+h5;%<~QOri=TKS?P_gQ&l+zwYFReY4|DINh} z6v`lkLpJO|O8|&mkHRSek4h*)oc}}_dkXBqzGl4f;X}=ml!}H3=`m4;sFdV5iU|+z z%#9l-7)pTk7>vg_o)xtaixCE;{Sm<3$L1OdC&R)Y%n8qx#tr@g@{)lo{;-H644e>CZcf^w+(DMiqHzG$^~tt zJSD)gm1%YZTDIS1FpN;@Ueg4rG{oaZY@jl#1k#?upa2M!4-1+F6^ML6qgluZ%NZ+8 zheHuPX`?k(P@PK*IpU?HB&He$G(QZdU{`xKSSo&2EN$jBK$A(a`fRae@Y9G?$Ls#c zX8b;$;6C6BU}SuO;e)yuzIM)ox{4K5V$ZA#0?;HM6IC+yDB)EdM(jn!^s{V_6wv?Q zv@xsFFb%cNg+M4laOe*(Sl1hJX@Y=uP9=i8vd?&2Q0cjnv2M_ul!xVE5w+;8oUC=u zFG=oGehWC4%;=L-3RtR@mu>!kwgi|Sq$|V(+Im2&Oya*>?l%u6kC&+xix3nVOOgk0 z`)+a_995Xy87>yRUC{brW2Rs&fCW|<%HBqcxr2O8$+*v6#i$1e-ODcSqge<9#!LX< z)?045?e^Qh>P269{q;|_)*54GvuWS=h}d+p84MQ{?)?| zKLh~A7`LyEQRTp|X4)G%RI4yu=iSSDJ_JB8nZjg-^Bu6gh0cBOj7bh#kOG2?A&QnQ zJF@_M4lDSrT{KMpu3&#ypTfz7M)GC`Pa84}s61{{zO9&0w`o)99k{&0=V_ESq9b2N z3i((GWFS9j(lE-Cit0?DEm9@cMzz6Ns}aj{(E?){y<71f#iFWc%6=6ZGMAx;;JYC! z!w{E1X9cmlM{#vn7D`3-!n5%-iMD61PAA_CE0h3|z<{p?wiZ!{I!m8KT%;1LgdS=W z!&Nf$)8`cUwj#YT{l|+;V0SuEKu`h$XlW=E>@AH$PU|XryD*_`t)RBW&u&^RgIy`{ zYq!#K#TsZEOQU9Yj;$8|N2DVyte8Ss=dGa_%*9qkJ-&@JaSV~9s=%v~p{9+ydDN&@ ze=KV;yN&^NSML~;pRP_!1~6uIvFNYYDvFc!Iy=BB0R*~Fr?C6qd8OB5PdQIk(*aQT zw`t>M5%45_Rv=6!3r-?;@(N-~*-At!1t5*ex2g|gDrzIoyCzDNu%dw0vG99?qW_lc>XHsEFD=P=l~ws*_r(p7GG*bD(Mv)F&HPd6 zcn1nonKRTKB5Jp${EJQ@g%5T==(AHQcBd;5ujo(=Fx{>|xxSN+YLUXbnnZw@+kn7E z9}gp7VK?2K*f{;D^ekQv#Dsaoq=N#hEC7J*0f;6O1O(_sCzA=0Oj6l?U74at7y0E70xGO)Pe|y$=9k8b(PpF;?%RN;v0%EGy%@q%CWt~s8xEPxhRARiZxQf-CTgNmsz zN9xa@k9X6_L`jU)a}QsxWBM6r@~XG~4~+Xl${FuMwY9uC$YhmW&msZ=n45oR_crrpv=vZyJG4{Nf` z17^W4VO;}}inCKkH7>;tMZ_EDn{~txkMW5Ikm9NQgqkb(7*{(cK~Rl3&pzBvZ>06C zCS`2e!V*^WC4=Cu`}O;J*wL$EikxLZQ(mKs5#{MUj2IngekrmpCN$u8c&{R+o%a?Ytsg1OQ;c z#_e_Z4Q+~l#8W)61rr6ukYcZnEJ_E!Nn;{mfJ4DSgbqv*%76LA>?flmqv;&B``%p} zWB48oEb;{S$yOyP3bAU%Mm9CqlIeY+(qpikJF$g%jFUgFTebf zaq|VT7L7pwLL-7~BC^&JrA^C#Sx;gjT7=x80+LRIThAx}uuceoE^V^tHpmCRb`+3f z2L91$EbOnEq%qP%b)vEuCtwOk5;g}D5+oVrRI4GV9M#AY)fR}H&V6@o9#u#Ktq2AZ zcD(d7JG@nY0OeG$PQ5K{HVkaBY*^x^k~2uZjW|c)Dl9> zA0AS)xec1)sG1tqV8!zy1N2mJ03vB;h8eXfWYDxy#X!UQhfp|wYJHfFR7I3ekOvPV zYv>hjKkDIh3m{f*lB0a!%h%?F(aNi|tI(wL{n2!qk9t#0R$dxYc9vC}>NH1_(LyoC zw(p|xVq617NmVP4sJGHTDnW=28JMYTBjKRN_S{9=MbQPi-Z}{2Zb+;W_l@i~Ni~{`!s_hua(G#>Ky(&b@=w;6 zR0L}Mj~9>I!%H~o`7vVM5Mo|)5tsC$T@H}>D#|4+(H3>JQKsdnYsJyh2&$B;Dy>o; z&y3LL%u<(h3KZ!oQOvI5oGHg%Makd%9;)VOmzl^4FUsVJ^ALg4$ig-x z#9P_MeoqLZG_x=SdrsS zggRp^(R@C))~jnuDZ)3#oasWquIsw41555zFMyEib20z`ZAKLFtLaH8B%&}!Vtj_Q z6z&m~XgOm#l4}zI8egn)yh8-O0TI=o6ewHN)aHE&@-h;pmxY#$6&iO!pUmE~rfpRw zrkojvm2_4b#_aJq$bUp!IS66y`1#WGL0Y>UY+M}hiAoAJ%ECtkI}|xdmlq6pJeksf z2B_NoG;aLcc2zxAoskNm?gE(k3w#R~()j|Nh^^&IZL#`VqKm+CMVDmfD-Oj%qr0yj z$PUMh#1G=YGOh>*6m%yu z39+cbT{J`FmP4%)Ln#Qv9(Jqb3vnruB#4!%M2R)j7|B8U%&sSz?1qFYP<0taMx z3r!^}!8VLuK~hAW2~u2Wwt`#+5OQG?wpwHdrEyjrfg07T#y?s!2(0HA2Sbib`NP83 z%wNg6NKA=Kq$-ulOEt^jlda`YT4z94W#-?aqSl5{l*VOuO(?%A`X$Ck1>N|Dm0KCCgPIOy zNE-^w^+&ayYEwHjvH}%GF+7G)S=6F8<^y-VHVsEol9fkN+-?J;aHQp%+;tJ|W@)Ac z>t<#tY(Rr(pqrsFCL**%wkO-Wl?Br|GRc#d_MhR21Igf-1!6?uy|4jh1|G;+ra4gL z7#buX|AlS5MNl8CR78^o!@?Z46~9B1?TB0*J`J!4*C^mLC074m5sOvVbB2^$VJ0 z((PD;$#aGEuHIG4=3BunuObsAWthAq`_wTY6VFIt;ZuCim9)Ny3*t!%vY5IRp=|+8 zCcuJf&z><}qa7uTXoAzDOj<dU;*xQGT>GugEmu5LF*3%iyRN`H#~7szzgM zV{}*1M+Wi=1e=dCT#IDJkXpG`lcKTmP$na7QDRJ9L4XS&!42gn^X$0AkDAO-D!;by zN7QOXX<#ryW9}8g2(m|m37|^kkhB-RrLrU2ioo^m0?|rMuwpP!mAHqMS0!jfM0&0* zMsTVaG0& zGpORVTis{I8^zV6zBnH>979~3e08(_T01GOib8(7^BDPP^x;a#!z`8NO0=3q32Ehh z{nACQI(AjNYO`nnDJd^YKkfQx1AfE+D)=Y8F z-pOpgiM7_gxBZd~AQ(W5?`Q&~U6{S=2Y~b>kMHB3ka7Pfu?J9?8*e_d$1sV2Cq|9FzbnKA%NLY+Wm=d)hXbi{tKhvTM39_C4sa09q%Qo+(k zi2gP3uI!!B&yGjqf0UU{{? zhe)dUM|)o*mC>d@2^F&)Ma(imLkM^CiXhLcRq3H1)XBq78I$=@(JOI#qj5VE)YThe zX*@hKHWva=77-Eo`8W}#skPM?ymiB)6Nqz)Y>fHC@GQA_2;|Im)nqbLC3cbms|3Kr z9BGk|IuEgi;#>c3&Ya+zNz6Bm<5P3vz^faHJ|!jXqHxhi z+R`$#FL}$8j&RO@uYf2X`wQXDm!wJ%%>U-VOFN%UDz0PA=0u)z^P)}4pXZYb3qdh( z$o-xmM(%u6=wlLt!kd5#EY)=sX$W^WSlsuG6RCM9c?mQ@3DL14oy~jgs+SyJKH&Ws zqRjr6X_Oo4@N-=&;HdY=MbMQ|!tHcp7HXa~I4X8nEm&$|5e#pH3Qi1oheGybIN|=p z7#pON`hNp7i7|H14R5K``iexD@iAu%kJJk?0@x1VSl*0$xid5C4gj9r7 ze3pW1xbu%t|wkober$!ajpFUn7ThHFu!daxD%VYb!* z!0G$o-dnfszkB=P&+nXkc>C;`#bQp@_TAL%**86SZ0*2#)1$|_%dXyg!DH9E4wg&X zF95ob)?<0+F8EJI0{K6wfRYoZ007x36IX<+5ac3xvT>>yI$`7pkEz67Vex!;S4_rj zU;;!J>iNhamUY~_Sk_tu1Vad9012pCYE3ylMbsW=Qlez^-Qz%g9|}p5d+2D4r72-s zRR$JOprcZFBlJE8px9?wum~829Q*1jp9fO;9QbL1P!?1)!Q-x8V)0H%K`y#_9X5du zzDMTp0;yJFZ~}|WN#?deSn1MeV?L^8&hphihbKWoEM6iSBsK5dw7i4lHHMD~TU9vR z!HwjPNYO=YrQRq%R&Gi)g5yEbWS&szhDA!3<`bo`QUVdl#<&ng^pRlmDiwj$h26DO zbbBdcGGP2% z3@ch> z1gtr=U5o-Ks(6D<;mNs4LP*-O51dEmsb>r-m^>tRU&2ypOP(GetIozxcCqaqL?~XJ zgZnHGFwm;#4Ax@Q+-+ptQAYWN8jD*2Hmd{+r%zq5vp2BZ@}k2%OHD9L>^mw zJpR3X35k z0f2Q1Vyy-0VYbmh54U~n>?hxM=C&KQ@4s#PkLcU0@yux6n68;n928kcHe;_!kF&_)4{Za0Q?;;6VeIc%q4A+0Th2H zLmG{fwU&D8p40KKm=RH|!Ne~YVni5d@%tRb&w!;piwMFd*_h*FY;u1n@Rz_M8Fq;X zn1DusP0ArdqQV664qgg8<*^g8B@uZ5q;CR69Qh0&C<{PX8XoCa%|1(OQ!J4B@sEsY zP8!*|GT6kzWZpa2EA^Sv>=amg>|Id)h5OVeNk3&4cW{qt&ukgxe}GZLF~0eFCKG+ zVoI7(S5jL`Bq6Z0VaoD+BBO_j`}8I7#_VdXovxOg3DCF|Ri3zMOfz;458*<^M=8?F zliUa`8c*&X1&ajV@_=DjJ_Q--#Kr71If>WNC*Tdq&`PaT9#x%I^$?F@YX-6aE1K6a z^Q~bSIoXOvC4`Wo4Nh2TrpI6dP)$WSc$Ba**EOx?gs&pvknG zPVx2|&;I>8AH3mjPThO!d^yL-S~uMT8+#{{X@^~ajeet-@7ELe*7;^ zJpLI6p7p{bkNGEiU>06a1XwswronEU^<#W=5Q8P2 zDX0rlTcO-Sk+|ljNZ>9DTA4KHSo2Rv5J=*PhlIv$^#K#4IrwAI;QZr zD-*&zOh~H|riSdaW{5NyV95}3Trt2E9S3a%LgIFft9ppmXj+6J$rFKletwAAyK`!a zHAqdmj}@~%;YtR5BCO&*a)5rxIdT@@`nadYB4IrS{OnMgl&xz7U6fm5R9MY(noE-d z8*U*D7|UT6w#qq<0ixCc>mn*VRYJw-&#R&m{+Rp_xT|O3EP$enjrv8|#swe)dd*gz z>P#L;Fqojp4Ua|tDwXvTLuITU91db3_6o09)-mzzsupitVbOPSG?s+%kg$y_+MhTT z$0GNaE!JvAwSvX{1ki6(tN`=eTWRM9ag9u8^5$$lL8{=FSF7GfC21uBN`p3j7U$0V zT9JsK69+kYV*(*J3ZNRLjG9G=qq6>@vYA(8lE#fmU}KzC`kG2@hy>DDJL6t*qzUpW zd~WNBAqP;AQR)W3K5*`74WtZG=`%P5d%b015~Hm;Q%WPdl0L-xC`ftL;f&G1k(7*? z6t6FZGle3>8heEpamHG!#y;&P+){kyq`|J7R|w&i;GjP)V#tN97|y_opfET$fvL0~ zV~SSo9x9{a#N`x3;c&a zv&e0WZUNj=Fql!CexP{W2a^%yHJx~-UKHS41uKp+b@mmBXd#JW{lPKJ(ZFm;7G44X zfI&mX(_$d7DW`}WM-Ajn<{JDZXP?$%JWg|nk6Wb&KZs%BL=uE|yWBn!fJkXuAg2hB z?a&my+y$YkmM}ZV0S15!x{nHm!cfMQL_>NW3Nt7L69Jf{Gp`*sEkFRaVUGy^Ga%`G zxk7QmyK;U_(@jX0hdLD2hMYFAqL!d08zHyp$3zbTvDrZ~9h@D&?37``g89Qdq1stY zoSA~s#8&Kc#82FsD$~s8ip9qr_@r5dit-qp*`h2>)o)1+*HgmT!Fy8(At@=mHV-UR zhdw%kO5?`I)@uIQj>ZH@5#wMa;0JQQ&L>{U&thbnMuqdz{$^Aup>(a41^mK*dZS7d zt!u(&t!2hD2H%_^6w^ibo@~yoDxYg7bCHmyh{yWCZ3$+>)eMcZO)JXi)RrG7BC;{G zbfY2$EozF`vj@I_KgN~WU5xARc8hW+t+lxjA+L9YQ`yXT44hL%A)zQ?Y@mzlwT9sIO?KN=Aht$xmtlhM-dbk6U_io>JZ7Leb`0oz`>2l8ZN0{E|z}lTtxIo04D4vW5I0}Z7m_AVOd!sb>X;>#lo1fCru_jQb`2(cC;*%9na{D=5Je@uD*kuItXLU@>s z0D(is7-JBOG2sK}T2+de%Lgd#P{jvlAV13|WMb7MKth19)epu~q8N*8`XW&%zhuS` zii|sdN!C>47itI*U)0fS`EeFXq76 zbDjLvGm>ZZ50Ax}Mo|wvh374Yw1jqyVP2uBzGGayL%?a+Jh}DbD1c$G5IStKK$b4N4zSx$1jl5tqc#C?a9?l70(q~%ZIk;!a5rj3lwohS6*AeM!=@# zSGB}mg*+7MMB9^$++aAsUwe$%frbLb@wu!UqbgZoNWP5ELK|J#O*>C$2U^1hGT>EB z%#V z;yE2*r3WEP9Z`KviJya2?+UOpsm}=hpOXjo7$NNnsztk)x&chcagA-(6XID z-9W>RXJlebGY6!oEXwBfW_>ij!%(}Wpe~FP*7630IIiEO(A#kFJFrcwJu7!h1OXW| zV?e8Rk0flxmZGIR9IcW!g11`aQ8IzYQ;djc1{!r~L2iaExI__Tx{*8~c06W%EC4K* zw6c7T=ONvIYZ8 zS(d~2e|`r)Wqoc(GgYc7fW^!@YvEm_!BZ2~hMf|9qp_u07pDx45cEnC@0^hOC5=8B+v@Iv*84M)lLtut&1 zlD^bD0yV!LYK(D13o9D$t!4d%uZ#q9$}{?7)+;Na%(}A4cq1Gw>WGlAar1vnqx?=U zLrRSN26l*43o?j^^y+}P=^&B^J-idHN?a8gc7qqScwkgS$tt|Dw>Hk1ZY5&Q7fTuj zH#SyD3ipc)az;>oP2ZjcY3vYhtU#!(~{m*$Q`6+Q15j zOUc1pNL;hv?=bSQsj|a{BrLg+_gBStU)^*$B}~Z6o&*rjV?N6Ni(V!$7aOjFz!fuc z)!9bLjY3>XzbW|ar(}#f+3*GB`WBN?1b{P#T`9qrqpE#03aKIqnMVeQGO3tWbzp_vQ|$alEQ>Sh|^pQ!5sP*pweRtL|~V+wmx~_*7>jho7?{W&rcmWd|+*A z<~vIyW556zZ|DKYJydPLV3)iVTIap|E-}Uc!jhKVq&swU`st6I{vZE#@l8K?*`-h0 zSoRhy8UQd!YQ!=i0P)9C2q5gao|qSzgXrIAA%gUMuh1*!UwHe>0NLbaC2AJSL=pt* zgh`Zf(zZ15+wQ)sgT|mS##pk!Z=`2tkd^$QzU+CHCaCwAjWNb_0ARWffPj zgDDF$rBKrFH8Cg_#gQ8WZ%R;Nfr7|}f?Z2&Ly;;M_%Je_Dj!Qy^)5WelDwjVJw8B_ z-ZTrE$rLmU&LJpC8b86p%mJH=jL=9{=}=uaiq9Oo$_@}Sv^-QVknG0tYd%Y07_i$s z8!4@5Vm6DbMW0gU+g4qqL+9QSC`xa~A=K)TI#mj^bXAIGOpRF1211nblwQ;qh9;t; z)6dkH>CIC-W&_fh6<1-E-WZHMzi}-}Pw6&kQMzg&AM~!Vh*rWW*`=j7d?88|lYvYn zQIEA4VB8QSwK&>LDjYM@<_nC}!BtY&7jbFA=KpP%U^Hl)+6pOJ_+3~SIzn=c6*VURBTs%>P(@>2%e4Vy2NOKfWGByN7)lyUg>|j#X=*+~m98{e`1Ce$z(on*y zY9H$#MVA{BVuqTJQgpv!?ORRdTVC$WdWtN$B!laF@N~?WK=I&AmqRhr1v6%zV-r zYstE&MZkiM&Lp2o#;1_{)>zG1iRl!@uUJJ4@%6F1`Pv5n066;dX1Q{tM`kz$?t4>u zsEq(%40fG=5sUXQVQ$7Ko-)@FhL;Ho#YHxV#u#HJlMVnTlS!OCf!n-3@`lF(5Q_zF zr7>9SWO1;!1hd0gY?{72LnF-s1%U+&MW;-m<8J?TEbim-pxC!CZ-3SaG60zP zyk)Yzh>&lFL~+V7EF!ed>{<7{dG?uk2MpK~Qk0w1q}YgI<%pQnO2bn!O130I%!8=Q zQL(6*164z69w(LY#E{o(+PwflwZ@`T9fP#4PEl%15E6p?QpM>sE{!43T`g-m5pwCj zI5aKn$!Hg@F#2(27U9TMp-Pf#5yF^8){?b@j8bVUl1%=e0ze}(29;8an1CRF$zzu4 zbjw9ABud8ptfvYx(%_m2_h9+DoACIIAdiP{x-{nV@?->?z^6(1C>w;s#_uH%p^8*h*j1V19|QJa0oA#EV#4xD0-j zWdB}&g`B35E=FY-;%72w@j-G!@q&zfqAvuYJpBlz1)W6~@SD6S2E=6RNo{?2z zv^1?WS#+^WQYj0W7$YL!j`@pUzx(h0{NY3U_N>pyF8j`O?ma8FjR+BjjHI#tI6n9q-ql__&}IX=1? zOlx`z4r1sBsXW8s`ft=z{84RbHDi>D2;q?_45ho&Ul7ZG1``04Kx)4cw`2-DW-C#o z0@9bT3yDIBGnlFRyb6Lbq9+Qvi$6C+ zQzpf%=TOQy*zzG&<%$Ry8U9}ZL|d{rM>QmtNFuI^VN_#ANjXc*%g*8qsRpf9@~cua zswv&8VYo~aR&12+L#Ifc0N4l4y+-T`JV(@SQrX6g8Ae(myQ~V8HYKj^N^kos-EtJj zx@NrqXbi5JPZ}X%1B#m=LD%0@7J-X?p$3+lWrlnhXY_E8r(U_9E9qWBL&W3bv5MC; z+Zm<;Yl!$572%$NPxTD~(R8i*)Vojp%{v~L!Q{XOF8jsQ7z^ck9xoQGyiIAZ02@S6AF&}#O zLziB+_shQVXg3Aw0RXMvvB$E+1_OPR&xZiQR&snsdRUu_Fr`ASFDn#0;{%h}GTuit zTKwXw(zU<`0piv~AnNgaYnVz?JgbPmU2OsZ5KL#f&b@?XI!PxniHt&0s6GGQ7Wnt? z-H<~348{$Iz+l=Ilfbt0UL=QMdGkIzk7b{O$1smTB(Y7cbcYK-;mu<|KmMsM@OOh_ zf>e*KtAQs+#Vi7a=f%9A%I5+AXwd5SqS-E#=$8Zu*tZ(cPalX2q%fq}Pd9XQtWcj~ z<${NUi20sSV{IK%wmb+EK5-STNd>L_tZpVV@fE z8e?K7aLzKHsykjOU`$v7vh=ssb66m4!7BkUbG-Q(ejwS%y*wj^!x?NUReGobd7xiZ z?h;*`Lcz-%my~y&LzeIvt5Yr!eh$vJ%poIT%DyD^W!3g;3q#5bZG7>fQOjn@tuUWD zVAiKCyaP#ByI59EQ}mB}yEIThiZLi(Tu(frX{<6|F@7~a?=MBNi%35Uzc>v=I>WoQ zBtK3rKQ9DK9inXL)-Yp&^QzpAH}ztR)w1TBclJi9ZDoT?9YaIOC~XvGFTD%g6apJv zdZX8`G}Ri6Yl2ic;vmFiL*f7I6%Ws&&r^LYPq4+7cLRn%{%7imSfZ}IZWtDvEeDk%f8Z8e; zG@O|R)>kTJYh&)8a9TwboELt}I3;qP@hR|?I~PCX9Do54brak=-v8DAzVE~X z%ftKE!So#>kwNRfMQ$xjandiBo162?pRniIFFE$)FFknCHC?v{0Kjg+{de>?{@wBS z{n7mo-nzYKW8F;9#(0T{+}kL9Kbzt46Z7}K>*1@OwCAd4tj(7M7SMm*JgtQf`#t#- znBsRANf*UL-E_U^$sGllUZLd!?38wK%J~to zinN1WZxk)1s7KfBw?kI$b3)YZ!t}ETdXjcjbk^S)^EVm90;$BiN|WM9(p>Fel39aR*0W-V(n^TZ5-`4%F173|0hYd7gg5mGmM60*YYAMW zSezKekhQN7D`9X$=YiRK; zg)yZ@eG2+S@>xX`?&lahF*_dD9vOgN^OTqL`GtlG||J4Wsr6$tf~m9r$3( zCRQ&S3&``Sm7Dl7EdU^}w*UlT{^vvn=^S1<5KZZDQ;!-RzG6CO^& zDKaL1;6OEvEF|oK6dy4+Km^HZ3?&Qbbv+ttoF6<7XU0MSMjk7BNEPQ2IJh6FG z=*cCDGX=y&h(^TimW*gaXM(izP#M~&i3|lFD3f+9M9IIs=0O?bYA#yzpe&JSnSE32 zFCc{%>kt7D(eI_giLRoJ)q{A>@7<6kIgC?fz>s1j1pl#dG+u~8DRE^hnNoXo0to(z z8v;P(-Nz(AK|-R*HNjct(_jXnOcc?$$rRJbP~~FjBn~w4y-1=shYF)Tg4~s(E2pJ> zyE)pNG-|tfx-%Po=}MAV179+cHd1< zG#Cz^y8QTTXw}tZL!g|jVAt-Hdd!rC0E@u2 zB!JuAyeAfY9SsT))h@>RAnSl}kSmBpIpHyEMO5NVC55+nxl)7_ahi!L0ANpcX9NQP zk;O7T)LUlq^YA`hvewqtZg-LpM>8P8)|G&3!h;M>c{OG_RsM^!IuS(brJB%CMcQ}u z5y^3nc}PO!W5VaQQW-s>?!$C8o{Z0~$0nk1)kCR`YZk5#4v2QVC(JbTwXDZ_5a=bR^pX>i-W4mUr`)|Z%SW%cq>8SJW~Y8`k_Q8O-^ z9zVHDtW$(ogVSgk3SQ80wzd+ZaNoL~uBF@Kl-6b$6qTP5c+&8KTjN5v zUc8bwF@mu_6RA{$;AE zvG+ds(BqzU@Ed>Z^7AjBZJ%BCOW1R;n@$Z_T=sT*i;SUbzF^PGzW1`X{nV`w-@dbV zV``}Ot~%UR5=?) zJ`{$jmLMszlV4Kp_6lUD;3u*goNlLp9Sb@*%t{4NMkx(?nuVU>Pu`Sp#uW;ML4rmE zC4{PU5RD-vB^Bu^$GM?W3z#75V^pMqFqN^Yo(dFN#Y%Ep=whX&<4?rtFzh9NM0tz% zQ65{+a?2Wjv#VtXRz|2wX$%|6q_+f{=i%vkuva=6Bh68P6xeIjk~dQfH(!<~N&zh=AX2C4lVt z7flr=#-Juh0719eSJ`88K{KH)gu~N7aPJ{q~-5ahv|MJ<^nSQcn*7uqRZe85{nQhbKMUPoKc3lTZiyeFI7wq}M=N|d9x81wF zL$fusL@6!ZskMz6nEtEbW^PC#4S-6-zxr) z8wBztbh54|0U`QQPsG#|U~hph`XB$8xvO-c*r+%w$AnM812F@gn+)b=YXL@JjCQ)1DFP6C zx15_CRiG(QnrI1HC4r~4NhO~EIYkHDq!~m)Os}trq6Sf59ScJEL?|3^GTi-YYb{v< zgs$rlaj{rfYmG6p+01|0B3e|Z7^-;WHQhls2JC6US_Z@0n8Zx@F9t1D=;{wvM4?Sx z+?P^R*HEG^%Lk27wP}cmR)t?XsV>cU|J!c6C=jUF#U)Nm?GCCa1`QLRQ&QF6E<{DpP%)kW*@NsQL$dfMhVBt~nZ&w2c_tBUl<(etz@)axQ8~rNnYhZL zLLsi!A#+v;^F{eV(~q~V)dK7sYdwfPCnK_otw7XEB&?(ogEtLR2uo@mg32HZ%EYu# zJLU?)4^o)g6eQt>R#5pcoLl=+<)!{ItI{IN$I(3a9%`W*OC|C)k?3;*V)0lpTZHTt z3b!)Ejb4RE=1-A9su)`w!}JtFgqmzY5M>3Sh*!kyL0eV~&Z?GL0Q2kZIKl~Y01`KZ zNmVf9S{RVKOLu(c^h5V8rjrR8V|(kUM({g{AlkEATTgrDB^OMxjnBRp|N#)k;L7Z+ZN3SGzz`MDB+e{m-icjBO6+t{7?TrRRh z0>HBLTzNysFIv@6m_mI(7*szg%TAzQz3{QpP{v2H00c)49yqdpKl(5o4I0Go%ua@i z?Kwc{-aw{12tfX8OjvS;Bt%Je*gGjgW1I2F^h49EEFp>g+&^q2ESF0nh+nNP%&GQN zO+?@-hsY~MP@7lZ_lVe;EmqsXJM;jsTrQ=-Y{eJp zs&kX^%V->evOI;O;YLP_fQ7`!cXrA%@jQ!_gMwC{M9T;l^Ok0i^Pz6)(-&R0bTJlGvdniFX(b3`*BOrhcTtX;f{*&qf8Uh8ly6Wmx$7u~I>M&J8S;)Vd|N z&8C1=IwisdULKccZKWcVpqaBY)7c_GQ6cAS@qb0gphcThKuVaz1%?ZvcNQ}$j~m%I z=VZkrD8TD>F-dP}K*SV=TJQ-&hNT4F5aZY|JhL#S^GIq_2$P>g3=u+l8YQMNG1g6l zIc?5!E99L+PY97v!p>(@vvBzYIiIUj&w>$6oQmpm^gGh~aqm;&Nl2IqjxnK)#+;y{ z9b6?(0TAlaasbuu)G?{32?eFR8WidNdb&Qa57Bn zlr+BuxdhmOUBKNppPirW_phx1fhA+eI_qu$5U}t2J$v`v_UW_#{ombm^<(xw>v zA6fkF&))H=58FdW_V;K%@;472xTHV-ic8k^<9tpBk4*RMn?A7IvVCto$sGa!8RK^9 znIIxPbnnjQ$;JLl_Yi>Fd4>{M5Fbhf0C5`w0VvZ#8)l0D6duT9lZ?NU;P988inuD- zc=uacvDl%E?h6l2+>*JnOKmyo4J{A=7^3*3q3t`eM3xA_bkI%Qb3}gc9)#`{FMGxL zhmSt)N!J6=bTUCOzN%pn!Uw8J&>DlZP$Ec94}mdJ^9Pi%!$Ze-Ij|?PT$f}E`8Q=p z+C?HV#!RQPMxQ~x#|@ZDqe$$9DMaKSGDDDI1jQ)8QEx4PE53PKJ)O?Bw>JOR|N5iZ zWcu8H`n>)7_kZkTAARt_hpxHi+Q&ZjF@4_~VtVgFAugaWg)CC03|Y&QnLeR(X}A$n2d;){{CAH#Y0{>ZL#l)NLM?l$S00#3!3b z6wWayK$wf-68JMjSy5dF`mj-tI5Re@ZNE7GEN_o#b$}jd?X>cZD z;R&|4>dXX{S}EQSXfKf}UF{-u*)1=$2{WD^jEd3{XYHI4TYAnPzM+Yo93fT_PFH;{P8<&XptXQNPy~A8|4==+g zNl>k>451Oh@0%Iye@P)1!qN%2pB8`+sJXkt_?kd$@k>}^TP#si$WDqLqrkL@9ch=_ zZPgM!x3g5-zlwizH^5Nj(332SQejC0rN>ulQ=sx>Q7lnAXJ_&vs_g8h%sfWrJ{TvG z0$RD3N2u&%WCw>N!}F6Jyw`Zmv`ngZ=cawNg<6oANbb zj!xBpOvTxJtF6>8MBLfg`P8RBb>hUSYp%Ka^2;uxB8O<0&@4;DOvETO0RJ4&V!rsm zhdy}Tc}Jh{_$LsMwf+?~gTmg_S%uE>PX2WHbwGvmrYLs%uL|vtgs#z_O zZWZVxw$j0sqhzuKOt7kPvpxlf$$L<(b9vuY&{>1rXk%{G=+su~9lQ_cfWV>#9+BJX zsFKd~yP#o=mu%2OS~ZPd$|4*Jn{NlI{5Dn0PiO>G_b42*YGvm!BC4o{kp2;s){;3c zCdy)T-yxQKebAK0dQn)rAn|&aiyL%U6cowlD z6hL^~p!8`>imJY*l#r1nv)Fc#%B^a;x!P8oEU}I%A}W?fav)n@X9*j=j}>}qhFZ}* zCb}V5mo*jDa4`T1t`(P{oYa86Gwe_*2xg_vD5Vf3IVv4rx(4B;fz8hdYHM>(d{wz5 z3~3=1Sx6`h#b8StK{Q8HeINT^v}lt-m?cN#lGU!ZTT{kHieMwAY=ncg79Yl`c`xBA z8cvRTLJ-HO-M)#JMl>p6xIl{!~ z^fLIvymSK)aP#Ei^zr$`;KUdU7JcYT7_AfFqF?UYSUa?DeP_|%_~8e;Zn8evW9JK? z9>HSYAGu&{eP7ovVKRfQQ~lX93qnt}+o=Z;5G@)61CtKc)^V}u7uyzG2RBP#5qAnI zGXEdCk|vGf>+QaN*F3G>_cy(LSQX0zFRKL7pS z|NVdQgFpEA$36B<|MJJb`W#T9~$r^A*anf z)03)}F~nvunOF)n#uD?jL{RZTs|Kv?QCbiqN2!*t5$%=feFJ_5A!q^$yUkL)*v6z@ zkl?RBbj9ML8ss1diZ zwo{0k^hBL=vQ>d7R;LzAGi#>6E7enMpi?3$r<_$Sm4c2+K1AbFW(09*@--4#0I_5M zKy`x0K6n+uV)mq1TXAZ0tnGd^*f7uGs~MSaC{($ru#4(XY$G1Vba#4ERH+qxqK?;i zqAjqdz1jIfd&kccwMdtnYgOQ(9*K#*DlJi>^6*j}9%=Ng_EHO%Qpklw<|SC3X@??l z7Q_)n%%I6+NmYr()tlJrQI2bDOj7nh0OP(EQ>Htj-|CKiLM#G{J}Gzcy4DrRd_G`^ z6b`v<@qsN#O@hm|s=XMjE{{3m0J&@ObR5O6s!Rm-$XDG(VbLK&0fi|cf}A5N4RRs@ zX&59jnh2j4ra1sDMr-&^-I3puU>00EH&A`5nBNkq7BW6c6Cmvg{wLX(pxwvJvrd%_nV+=w^%k7cQ@O{Q6VB;>Z_1@8C7pui-jw zZ`y8xh!&v>q=Vla1_dLMB8CIc245)=Gl-lH{K|M6W3sR-zW;P0M*A&PBeN6YEaVn4 z+J^aj=Yjk0JO0pv+h?`_!1g_aO?Da$5siOtDNH4lWC9|$!!g;uH^!VkbLJ2J;O+O^ zefK?g-~GhLKjBNC^PJ^!Ihjm|taWFBc1!2_T{Y6AKQ^DkQxDuL;Fu zb5mIw-jbyhI#Y2MTFuP0K&N_1;LQV|EUc-droag+ksxO9O!SpJKFqFG;zF%z zuyQNqtwm~aN!FE^qmK3$h}t<%nR({RLE(}lT;pZ-$rw`h;(!>5Fyc{M3go8_^+cUr zhO7_f`=P3__xQpbX7}FpV6_zKQbL3sZcGH&inLo>4u^Ev|63BKsgFF+h)FhCa7@k2 z85tQ#p(lpuz$d*?5||cns#F7#%O9q{!FR>=QYPejaK5&kMa_`raY#!gbK={cQBjH_ zL*YG0pv~=H92gdk4URpcg^5-m-tLp{5OCauxmM;A%D%6hgJImp1DV?Hi|rwB`GRXhmd-%9BC9$HY&u5&#Hnl-+UC>7|$(wtPWW`iVlbQXyugHs}UwQQa69 zlFb$KhjIV$T7jkNZPZqw5KH3qo^sOd4h+~p*w=8k(j;j(#2L&D$rxM?UydgXkM1Yn z!T@#a%yM&kfj9#~>e1c1BOk@N2U{$Fb@{F(SS*0hB4RheQztgfhJE(eUGTVP?X$gI z+vrZ*-{0`QI%31MFS3Ts1-oCj90!7j`;%C>94qkQhTW;%GM;_ z%wjdUTFw>LOue=fP0iNvgn!H>0g&gGZb=$L#PCKqSQ1?GgOOb_#LI~cqz=HWU_sRC z)Q!kq%t&o&aNO=+iw7$6N~WNZ^hOrNWBzrRG1VDlx4|1jhMOkOW8$BGq}HDMaJVQ= z@+ye?M4z4$DsjSy01_bIIidjQZS_!irK*wIfr6PS)hd#t*rzC6W5i6SK(QNH*cEd^ zKBUI&d>vPdojHoyH*Hnas=n8A5M_?4MQTtgOo2)&I|C78UeHPB)sTM|na7BwFGD#S z>Kp7-{=Cc}*oG}FSB;st40x81He0mGT2CV;7{1-fPo^!yvdzQzszv&UAh$Lx%7nRS zb&VsGCvn8HYz;rfxCfRgl|s}6fG z8mJXG)$`tw3kZ={jMhXd0H`|})C&+g!GlvS9D50un0Y27u`?ff$336^yAx|`lgR|hQrC5e7SM(# zC&(b80YvDnT`p}&5aldzzoeAvH!j@LW*^dmV1V2YO9B)Y`=2XehgkRYWCQ|)@sVtd zy*NN;bIO@$sEK(avIqoVp_>}C_`$cI`n@;bd&`IWz55Ovy?9217Qm7<3k0wPwzsyY zC4k+SPWBz0TEcs8TK?J(KKQA>+I-~?9=+~M*81Kq=P>C)h`}9TIAEb!is#}HyHc3Z zph-?pD%Gpx0-Z?_ddGd1BdZc-1ub@^M{Ac$yI3w4%Y{2hX-5>|ahoy5KL|r^%QrU@ zZxUV4u;M{s0Zi8cz}ni{t6uf$^N(G4*=3hL^$VUlpU)@V#9F&tE{!o@pznJCKw~I; z&^{6|o6R;iH~;vL|7d4>=Lt`I;`5&S+^wxG0I)=h#q!E4uKe1s``Z8X3;*f8fBD{T z`KE6+i0+f;wSa-yjc9vy9;F^UVsmwrvVS~Tk8OD*XXNg z#l8}%5ssIdQ}KdL%pfbQ&`2HAw7fA*fum5*K#*QRPlEQw!dW$F2Hsix%uV4{`9uwjgpw|efVN=Yr68q<^*sWQP@P$#4B$coUjjUvQ0?tIpZpX3}4 zY6XFj(Yz-w!ISnJBZdcgRL#}ZS(VA?lq$1BzFY+ZJ+3-)2mskJrwz)VWdx{DR$vzZ z+LEJMP5`N=01{3kQ-Ex~{sbSYQWX4yyjD(Mkfi07nL*RKr3GX^IS@54)9V5(Y{~L~ z91ev(X$C4E)cLkujF@A^x5`N|6$2JwMeG^sd>j{Dhc}x`6pcV$0K$YOTFcB#5ZftI zk14YG@B?WuIgmhK!)gtRS=lr%=fcl)m5tS*xm6II#ZCNtI3Jo8B5*F7hf6TASxC(< zIkqLK;1}~ULK-g8&&YwIIeE+MUhG*cU-RyBZ8prnV*oSQqGyR;!m5y8sdE=JG0{Xt zOdwK5`nrsc_IL+dlHGu#&P746O2nBo$Ds&kYvK?|8IR(eTD^WIuZl#1b*p$(9wrTN zY^6v}9j?UdF!1h`@a>uPTxE%yltB!S0EjGDYpr`G1Q09`kVQ*i{b#iOGb#i~&_mZv z&YWJ%7xo3;e8r2u_u`F%xY)L{weG|3eBeF5efQ25?B9=oy$NxcPn`e^#z#P?>+EEf z1!@35NCq*yXExXffWJ!2WtnR@5`zC@0D3?OrayZ_w89+`KtL$WC8kse6EKNl463>6 z96dm9!CD_CPuGx^`0lrz{O><;_tx?5qRWpg7iOmi=%EACb=_jVv$egmge5>fo57^R zMNiAQMbjNSIt7@#?@v$O_Sv)F_0tzW{T2I{XqOgD7i(Gw)^E2TtNDSm*~AO&3)+@W zV0Ix*hc9Rzn}$(I%ozkV?)Kf{OVnreOX-T!6JdCsw8$4;L=k7_NZ$m*rGszC_f)NinyR8CNBV>qnC92!+RmB_b|{$>ne z;%8$z12uyDAhWrZhXO`IuJtJj0Z=j~dVo^9wTQA|zcVt%gt(zJ4^VuCIVV_YyqZwQ zpjwadF;~S+4asHXKqF@vQ_#@{axNt!!P54)Ky7c3HI-CNoaBQf`PD>8q>7OW+@y)I zn*C3JAqFuO%z*%1cXA@D(<}pfVFQJNl&Tu*SqyL%2ob{2j>jKYwS z4bnl?nuv?(Yvk0d{s;k@c+7fMrf6(l0@O{gyqDi>s}vVYffjPk#Wbl~ZstZcZd|k) zxg^WK@gI6LqC>S%9c`q%ppFXlXHZAATTyIQN%B24!y1VY1hVrhzf3p2Peyq3s7!Ji z_A6att{5QRL<2AL@zIAAQ<18b5jFsKQU>_0FATp9z2CVcw|Jp}Yvdu#XX zo$lE`1pu%FhCL=2l_<&%|zXCn$Mhl6}eClUr>$LVOpl$T^2 zB&OEZ`kM*jwiO4T;^r7_bktT`U(CsLc=;uJ@40RJzx=?xugA&LU%6*z$6A8UWa1y6u8AK@ zW9QZebUK+%r;nUC z{<)iOy64_|H_x0s@7Q@)Uvu?!*FFX&u(LCtbR7U7xb2nk#L1JNz3DT@k00OO+P?Ub zOP==hryIl@ZoFYJUtDv|)dvqABBJ~6yYI$Ned^G`gV$Yq-QN9s`{nX;H{brb&wlR8 zE3bUQSswwrHx@{^zP zm0$HD_b?7oXAB}@*LBxl|CFn*z2<>??*G`wKlY_x`Xz{HI+Fx27slMKqR<&;1@iD0 z0N86*P7Z}Dig9txba3gdMJl{rz!wp`?<-7VLY@H*1`pfmaU(7-$S9C%DbrwKrOcx1 zC`#|DOhJVS+U!Iq+KxT#2!hGthX;r`DV} zRQ0Ud26Q>X=8=;iTZp|&2zhxcuxr2`vWX~aC9w|%6N`ef(hq_`rpFiJ!{ZCfH| z6fJ;Cq?m4bDX(kzGl<9t2JmUB?5R=|`YYX7(S%3-0xXYc2E=kubPIXoxpdi$(;&K) zbKxE0q{iAGXA3ezP&ghwrAQX2)M#zcX4dLo3aJHw(41XKiDroSBD#Y(GaiA|9B(Yy zkB4ZcuVyfNO0#Yx$~pjI9gRUVNURZ**KCq5XJdK6H67h%;_h6B58oPlz^ zP`_zXonYQSw_5(35I}4`CRbwC&ZzE0+teWcJvdY8I2H7i32K$d;Pw5BCO`xm`)3>b zCMT`!3CO*rgvPn+P?914)Cf%wrzcM>Un*e}>W+lwb3TzvHZoP6K!KXT^& z#s2*>v}g#((tO)qdj5fj9@u*8kKMcP;AM}0;cREqhOfKFPlh0Y-xQ|sTL3nDzCP1? z;zxu4#=WTKI6`6Z*_^c&1Ms0%-_E6{lE3w z|KoT6=l^`@p@-eYFM8H9zx%b{{gRiy{MfOh?r{T${X-x6`(OL-zxGFe`1bqne*gdu z>^tz&Kl3w>ef)L*;{W-7XxabUpMCRJe&tv8egE!vzvuh@f3JVa6R-bw|L)&E^I6Y0 ze*E~a{I_3u^S^!bSH9>)-~Jun@smIE(>H$n6AsaNM~-~o>t6qcH@@+}{(W6Hna_7V z{h3c8!ey6T{=_Fdo`@zBgP2|lyZnmFpZ%OK`So9Y+fARo2>@oZ85)BKWaF0Hpf$?w zMj*c+&5mI)GF03m&lq;mI84<`@(e^Z>Hz$CJ>96P0HK_6eqLn4P~kuXi^VNjKCjwO zeg!f=uKNgqRIQI>-HM-8^crE!SO^MWS>(#U71H?jR-+gJnpC)JZlnscDhj)9B3;S3 z+AdvH@?ciOEXzmsLfzS7Z1XB()3H#<{1K zgZ~M7N&r(-23u7!@u;ugHXvmmz+xoM zCMDWkF=-EDsc3PsFD-N=F(NQdYP2_j~YeL5nyJCvp&!-Y43sQ z!Gn`~`)zA&Sbq0<5%~fHSt3H}&`eLB+@7q#%fIK^7ryqqZr0nL_U@nF^fxE|*T25y zkvq(hBYS)70nx$)z+z11!{Z-hkpQiQ<($^nyV<&NBzm%Z-9$RM$eXB8G2w44aWW|A z)di>B+HcAMm^>rnWf`n!e_}zhAZOwFe?~VN*{QN_k z_jQ+Dw107SK1G9MdhE&2$*r>&J%0I}KYh^?pBLZ#nLhpHdoH_b&u{<#@0_3Q_s`aQ zvc~OoJzt!6c>Td!&iwAbx&QT7U3};|v)Cq_0FmP?0^4oQ&mwLF9eB^;L8ro$=FKO; zIgJo@v}W>35T0+MeAQCJ=rEGGNcu|@7S9_`LW2Nc43Z@@#&qW0fAQ|ufB)-lzV()) zM~;5gi@x%_3y$6V*_%J`!4H1ugMatk-}8~5c+-zxaKZTw```TS-@f)czw2|KyXny3 zgU@^JKRxe)^FDXe%|HCZKX~k-3-7$`jtkB||K!P20MJ`|>eQ(-Cr{pY-@Wts4gzd% z@0>VsvYT}8{j2x>)!+Qp#7v+6{O3<5lbde3>DF6s{i&b&$?4kqkN)rvA;RY7=B>Bg zLPST89$8ykUn~{?rt2^&_}XlC^%YkE!d-XW+4sFM1_9hNIs98#<8{A6KM zYH3wjeQOK}bVf00=aY&R4HqEfBoce+b{h!P4(*}?N5nmB8&@EhxM%V9mfaE;K0;++ z?UG6ZCspmN!YMgf8_omA8W{qtioGo8WG)ZD*B(ki<$Z=)Mr5&erE^pVa^oKf9#K2U z$F5l48LS*(aYtU1ocNXEOc{>lwmAI-;5I%xlU=PTBLj=!O1dRp>#T;*g^CG+{efEA zXtZ1uIke%_J-474o$Au7>!?C*v|50g1NYaBG9nP> z!7UJXqtf1TQYkCJxN8)ttk~p6?2W;gi@>1Nk$ma(XB1#X87s54@@mjoD7aGojA|k! zrO3YBik@Qfa> zCPs9Q5f~Ov_>^T1omr5QGS)3>1}NbGLoFOq$~p>kLQOE%y!>cRksQU5^4W z65tmQf?fyP5+gtx+k({ub~5bT(V_i-ly2SzB9Mn@tI*>u@^hY;PCy#bp;?{!737%Wr@C+yCf~|L|vj z?#%p9g@$hYk{vwG;^U7GQmS?aIrq1cHYie(3n| zl-tDVOi>Qx#> zoD7lwS4ADC(_p(wzX*j($b%5_HqvKFQYq{&K5lp#%UxcHqmeDN5sd0qILFckB~qDR z-bFZ6%f{87Dsy;*O%26pwPC-;3E@d!E+6h^z`92-3tpURp^p5fOmDNSvH$ z4WO_g?Nt?|x<3&nk+QgxorXX z8Dl`pZ{#?pG!d_!z%g9XZ3=yC*yrM^kGuIfHkUDc@Yaf`k_kXrMwN|u(}C1nhURCwE1 z^zlJ@P4*(^+gYS)oE4I)R{$|SFO&s3F^6DRH006`0}=R7gs_FjAd1LkGN;3J4JoiE zTMUtjS#)hgi9-}_<&{mDLcuuZFED04d}(rN2;My)FYQ!Zxl+#fdb1o-egVjt9F`Z5 z7|sDIQ+9R*(ft=&22dnqtO}FXz>P|oZuqaLenqn4icqv!4ROj1M_FzvXaKg4m~2bDZvYRO1V?8<5kL`YRK{E9$iCVNH#!4ggPnv1U5*UeyiXNhS1=Mm8u zA21oD&R}3(Uzma@Np6jR0D4RPk`T>Z zAK(6mKRspUlQlzq-y3q<^MJwr?CG70AHVjbFWqC8goxOoF*u!JKZnUa_`(+-Jpb~I zvzz^NG66yZercyuV|)16drsc-(ZzJFBO96G#e(Ade8eg-al%RKQuRl8uwa`?9UgO|sN4myX)yd||C!E*C_E1c;_H6CyhQ{0o2LCw}r9U;Sz#Ad9bj z#n-&y4R5fPPMtaR+0T6r08XDg{m6rlbco$_iimyRlY0&>*#z9PX9ECEpE`5$ zL*`8##qBnqkpo%lFNox*<4^821P$DqjG(76 zlM<2)xe8MV+@;t@IO0;V#C-M8g*+iD9L4{}8F8R?c1$Q2kt<_dz8bEY3YVOLoXOn! zsaA*^zvv7h+yjq1WY#q^%Qf}nKLs~Lb5w8%Vaw$HrsPEg#e8avFd?WZlFHAto5$r~ z$0(8K6D+h$qxkg8HEJt<$(uM?M!6ywl5_&b6Q8U2P|E*b+LltT1fK=cqO)Ao_+MHT zedzYnmw{#`L9NNirZIU?UDK z3TKUG#>JY2Oq?lAN2T#tWqiQEN0V^0MfgJ$GK{1O{APOI%_*uU?B$s>0x_TU7t zLt_jX_vt7LXpA{^dTalMlW+au$2|Q-hZl<;El$_XatF+eUh+Lx?OC79?P6!S*j_A_ zM0NrH@aKl$YJio2f1GI; z*is3s)4;l~LU0c#qCuQYI{-Lw^28@T@d;zh)1LP9um0+GVzFFbU)!0_H`X`4 z?c2WfcYpVPJ^0YWpZNFtD6AvvcOm830)J{biS3`r6mN z_Py`@OEls6N6L$jZV#ym0>G75UisXw`0^7cPRw`a^ZCx9LkA!C*z1UBXJ@f_b_)O& z%f-&l4g}ZRTiZ=KHysStz3Rb2xJ`8&j1ScCT%m_{a&)#i(#i%pn($rntQEzWR3EH7Q-n4uz)XoLKZ=e#5KtI2AtB@lw&K9)6@IfEc_x0 z>>_?pJXApy2dYkB43}EVs$R-7V9K?Ic%Q=foQrfdfKXQ39Sf}n0M^%ZPp(2zRUqOh8?Wb^yYI4<$V{Wn8JXtqPpOiqrtAE#{Dri^{m+jR>^t zqq7q@ z>JiFvDR0RH6+kOK?xvj^5X_76z+^=j0hNs-0-gi`3IW!=Ox^JEUZn^XWtxU#ijouQ z;guZ-8{)*cE&&$kRHLL4mOQT(knnMs_9II&#qG0%V!gH$jXan@#1ynY$aCrOQoOh|ZT5vI1Z&SEGRvZ?!x)_@!ZyJ_#o-s=lL82MvL!`=9q6%m-P(3eaigL%Ma&~3DD{cf! zbrn`2y{1Wp;JFHWENK8hOw|zpVL9(Fd)&S&AHVnEJ09xi)|e@}9fO1QG840TcK*1l z4qb8m9@`TDc2fkv**aYFw78|q8mI7qyS8um%afCh$+YX-hJye|wkO18PxEcK>bip$ zKW4+Cbx(^BC7DjH8`Hj1spJX}i#6$=w9w3K_-+_A2MCam`1kG*Y$25aQQQznqQb<+ z+!p)nG&PJ;kAdULGZV=)A&>$}uz%G|d0CcOSpN~ zF1CpFK)-~pbIt_a+dUR&XTHSF>r0pwl7-2-IlF^5y?^V>9sRzmjO_`H+eI58N=e)& zhyaLvYRTC|NEHW=ww{f2Lc2G2!ICB0KnS%`fNHgXuvo+k*pJ8+bA`V22v#UAS5QU_DMWK|m?5ef~ zBCaSUkX2qPW+5SRaYv?oMd+7kiJrNLRwA*^^Om$H;Nfb9i|+#(FBOoLS&Xccg2t|l zdZvtHz>KF&9jf4r*A`wuB2YpNdUQFsLiLfNBMryOA6^XnIqq2v;Sbrr#N9~a- zZ58z46?$3VLw zkt*?GenqwlY~3=qQ-G~10hXSY{pGl_o4|}Q7=iel0!%rfU=|e$;-%=hsJM;*2)Rx7 zR3W2eIKqe!2Q@$MuaEVVAbL7N)FrwQQG>P;ptv;BC64u+BOA(h=^Jc9{;E+K|1t5W zT=Z9TRJzL?W4qJIK^z4x49(*k3fz;{qNo%239}g215GssbbI8wT|&q%=-`p*GoE|l zzN6E#n@i&@i}ibz7@SVbWP;oC1(7u!PNybPT2Mun2mrRuF6PTck9`k4*&ZxVkJg$F zx~;Rz0|(Zg^dK?#QU zxxmR+POsM>#EejHYr;7KMB}08+7@;JGJs$a!A#73cP{R}W!X(SfZo!QsJFBr=mC~w z`+XaeyFWGm*n7{I&h!h~-n8@WezDWLQ0AV`Zar|@_MVMtZ*80chC0OBdUyYw%iC_; zb_O+uWP-5-K>7E-!Zj>^7psT>wU+_7T~{0qXDt6IM>DN)p9#mVL-)-9WBj5axJO{z zK(d60#u#g@CENE)+uJ}p8Dp&N{f-6%265RhDNpk#)+7+YXZIZdn6B$)vuq@O;O+6nODFcu!whz&^B0N&$Vx6eb8VuNlvbR-_VXUT?gGeip zygo^OX-prEhviIifwazkNWG7@y`}lBK!dy+Mgq8ygtrMXA3k2e_2f<^BS7Z3Fc-_X zI$6{f5tTj`Rf~aT=vNcd-I;Dybmov_4p2Bgg&U}2uLwx4hYcyIG}^8IUDc0|afm!z zfx@lFJ}Z%@uSnLilS2WLZvUAzh^Vqz3<1=yh~GrXt_rXmAsZ8Q8-zAF1=E>X6Vb}RCITs~JcsPM zl_0w^N=++HUt(Bz;rO*>7VkPqaXTkNR!jtrR|EFhU_H=V65w!6)fcClk!b-?eE;{NIFatVEJdrKSZ6Wm$rx>?`%T@OHj zXsre8jI}i1>Ywr4!%uq7UNf_>1T;X-<>#IUJ)M|;n~mu(Ry-9z@_39pXb=c2g{w`g z7<4RC+uYm4f0?wf2V5Ai^cQ_uCGLe6XA1sN3k2hy*f9tICr=9yMO(mGsl<54jJ1o09-{Wvc*g$ zc>3h>;fJ;X_7b_$m-}RRD*ZuJK_zOJ5ffueBTx$#6w~hzJSBlwGd<^~f()nb(S0Ez z8Z@2h$pXOo+S=hmhb+K7_uO~p?3uN-we9Wgs8;vfbMGCu-?6r~zHk5jJ$p9R*Eay* zfqU=0?e;q!^O$QkH@CX3o6V*GaP!T#Y@OZej&@eoq=-yd+wi0l005XsQs4L7S?=4r zanVH=e&S;vf8fCf``#MU^?lz_=k8QY5O%h=Z@=Sq0J!jyi;f&Vyj(7ke;z0TVniUb zd#UBBp+*3;u!cwT!bg;zv;xrcCr}X;I9j00`n40Mc)sjYTJ<7XRY)C7oK$C_MG=aB z(ct(DCm&lKn0eb`%Brhs%mo-viUs0GYonEEs_Lndk!GIHi!)O_-5}Ja?iSZ#sO!NlRA2CM;9ND82rNyc- zkSIJuF_d6pqS zq3FO-i1R9Y#mq`d!q_N^e#%ioxHDvkHKHM!i3rF28*|a7BdF4x=xRL^H&W#VG?BoH{teTr z1uZcUQ=WPrp-{D1Lx`iY;Gv|dM9RsBB=bTsk)M%tvYg2v}+!i-W;>MG90=8$*#+8UX9Z- zPk+YQ;P8ebX@$TIVWX-}UlIthxJo~v=}^d_9r_xvz? z{wg`m1cv>z);j|K8WzSCbJNcNAejyT;LOS1&h6gy2?E8ft}QGPj0LiU#}2LC@xili z{h>Qw{X-XBas38>J-rQgesbp>Z+YOx_n$p@U=Q^R0z_9UBLBf`Bw_w332SearQ=%LkcQ{COozOB;4;lGFn;1D@hv;4>J9yyW6QA@1M7;6F z8~*n@-tkSZe)Zbgnp5iICr`ZXH-6)hM;^K6nrol<#3!xIW>3EUNdR!`ZMXi)TYmYc ze&(moKmU9O{c|_p@;m?YcM#E1TH&DGVe2=d-Yx;aVDcc2@_xB=YwU`22M->8>h(|g zvv>X3z4zUF&pr2CbkPM{+uLZJ1Yy^8r_Y@I^rt?Jh*w;3)%wQz=H}+w+8SAk?@z{j zHCqeDFTuW=1zag}$oO6jYRgf|nVOrA$HLPMrb+SYSX z3D&Z>N<2_)1stK<80bv>)!;jv4^}E!u(8p3oW-NEUIOOGll*e)Vm+1;%RPpkMr1#R z6(XytgA$?HHVV5M)KU^rxG5J!SPxYVc~wPcOep;65Q-*braeSl9g{P1?a%VHCYH=9hMauN z*d|=bF9cb+Q}R%iHuksTkl29Lv@QTu)TW+`sqRb^M9PxNSbA2EWK_qbI{KBCb$8CL ztmA~^fjBDmWVOywPD;YX7(uFq7Ao~Fe=e4(o>feDmSQ$rCcG7DY=wq^QpxIG!*?H zOhEMm0DQNXN>EUL^Q)yR?DlmT;xyvRnxAeo&aRQM*8tL@_)K9@kPYEAi~!u zoIo}m2x%$-AsW~^+duis4?O217r*PRpF6YF?Ooq%Es?PX3|YHx?`-?{@^Am>9;B)1 zx}{5ISt9p6viYXPo@O&Qz@Rg60E9q9y*BK?-Pzon967oVU}4Du;sjiY#dyA0zz+Kb?CgZ6h8SPG9K|Ph0%t;W zlf9}1X&&*4f1Nd33ju{dw+VbfN*xbI2>5&ZL8=X6*G;f9ZejN9+4B`&_Pi%v|D+o~ zal?Oj^UobTu>aX#{Oq;0_2VZV{-w9Pp$`F z8-Ddyer3zp;@wFabF=q zpz%ay{z|iGmB0ciX*h6CLD5&lgA9I<)Y?L7(w+rcZtk_i#i8e?*+^jWC}6aj*d+N6aUV+vyR zf%A}3+!R5%&KXr8Mb4k&5?b_%eQJ&4%D{s!;c@Sjq$0-=BR&wbW8L^kVEoI_3F8L9 z6PGAmSj>o@Nu4WdCp(wHevEb4457xxTCR~xQAEwjNH8_sXLlzH1)|uwl9=%0jIcsy zGVqL0CyrwbMeV9YJ`h?NMP-r z=T1j1fJ7&ZYRKWP5!ZhvB;r#|&5Z+ycW{?)(w zS0DY@$G+`bzV$^f{K|_iz2pNQ{G0dx?FZM_)?WOQm;AsF{EPLCwVnC=G1p!9BR}?I zKk$FO@t%9``Kh1!$)Ep)U%2R^i#~tXU1!dox$2s0@4o8}M4U_}o;*uLw47UO!M!55 zTrP=dv0U1|?Tz%JF_k7?3?|=C#UP1uNen9}U+1hfs zxZ#FR+;{K&S6zAKv%mORi{%ml`o0&5vi$eeu^WUl{6$yhzm(qEBtMDzAvql6PE~Xo1z6koRgbh^DMQ5P_%4>Wg=)vqUSt07cKyxz~38$DQa;wfmNIp58G^~ghVYo+~i5Qoi0<6Xjb}swCIrFIym7E!r zJPb+Bop%c8TMDALT0ybkewt*Alk)HmRrEZlV$;m9aTMy;mjfIUgh{8!c$K=blm2H> z?jbSjhWcqygY2JcU0Ki>ER^b=HQJrf!7-$S0Qw4Q3e1MNpTxs<28Y`V7@CE^h{YM- zKa3&|7@Zbkh6jMK_LKzzfFVHWxAEYG@HMZ0-073}vA;Tg#B_T$I@=Q>q57j+Y|LbV9)>oS){JpS(r1Y7cY4Et}EJ%h3guY1|cUiN>z@eP+=a`D;CErZ4)eeEk=dF1fnH@)d6Zol>R?Vat<-+lLa$Ikze zAN$c`$Ik!0@BQws>!!Y}1Jl`bV}0Y$p@Wmof2<0PSzlXU>pCJZXbhUJ>kweRTyAV^ z9Nd3sZMyC#yx_tMzVan6`T2kUAO6o@zxUyXPi$N>+K{zTwvuxw3|mP?JugSgt=s(7iE%!?u8(~{EYLGi3g^JW@EM_I#F z-We0K<719$NCbp#Sd%?YW4M~YMuG!v$efGs!n#F^$EDF@ z2~;6K%9|QVOfHkz^aCi>lR}j0;=Bi|l^!^SfpQ9ocGG4gNz1|n_MJISeY8kyb<6w( z^3G`#U#$@@s0s=jtzkTC(8fe5gR}ut01i$}tr3CL$ZOj%cba!}D;6Z7wKZFrBMaT+ zBF$AGN`%NO)3VkV3Zv!}f*oIN3gwFc$XWjI59<%;KX9%$-P{QBC_iv^#}#|6B7?>t5NvNRmi_#BFF*G6KX~cJFOGY6gh@bx;&@@DR^Et#DsUt2`sp$^=kDEjJ79T$hL;5S!hQg0+A*6 ztVeD@k;0DhY4@IUYhWDKJ_Wyj>P47Q>VBa3oVGrcC*g?SM64utbgNLS!Y_C|%hY40nW0jjzA_yM| za~!7j&N&6Il&~g`Mx~7S=E^$fopP%9p?5zy0!CZolKsxBtN( zeAjEg-Ibqav)QLV^VvUn=eyQ7*1zH_UeK8i!Z*%60mI!6J!T%*i{@zE;hVp1Xe zIj4ljmo<7KLmCmt$m+&GXMsZPR;b39FkmHa{tOAb*GAyd0-z~ zw(BXy&lvalkf(f@q9`a-p_P{elWWp7M%inPr}65g-AV6m$xyB|e~KkmtfI#MsLp;Y ztFQpOG-|%0njZ;n zGfogyZF;+5x;37nbdBbAg{3s7ZQoIABg;c7t`_7V_q<2j>5m~#?Us`#0u`=wC=N=P zs$ne2W=fqNv&CHDIgWe|1C<8(?J3c!=x{YN5z#Jbpo0QP}; zLuNUro$bYRoxbK`pY<#sS^{|s9?UbkdH$I zu>KQIZm*JbJ$oAoBlj5Nc*H}l62t9Rs7dFFK>kGBTZbsrm0U-|%AkJg07SM2061{C z+p`Bx9tW7A+Z=cTu)Uny*_oGr*U?x0*affw^KHAc=_rT-L_lb#^t7*CzxWAP{g>~( z^~S$Fecqvi1Z1HHL;|zDV~?IUIq!lENbD5x5PG3b+z_TCJvowxB$OS>m?qt28k0)J zCT`bYI2q6H-@pIszV>THe?;v2{^E-+{`PPG4i0T=a|;o>u5-|~cXmGZvA@6NmRr8; zE57_?uXu6nwzD&T=b!!=0IaXijvhS<0A%T@PkHK7pYl}Kq3`=Wd-lHQ1uuNj3tk8S zi{%0crqijL@MErf%#Zx=4~dC97k}|TzxCJt+sj`1)yIwXYqF#zc zkWAexZOJxt9>dIAzKl-%4Gnx ziYbus0V{}L9to!5>ZG`8a5j`;R^-)<@Kb4HEMB}?Q(-}TPAYR-iE?QH;!ibA9+Q-o zj12%$EhZ|#XNFNnk&B0Jw>5J;XdkL7z$OB@4b*wkSQ@B{oTy9*W-vOlDKuq9#CvLL zQf-Wx?x)Pi%8Uwf(W+DEte-R#sv-m|3Aai*F%q%hj?A`v9LvO_nFhAc=c7l3BC2%N zfb$Nj@Q_yX%~B6`Q0fRZ9}XCdqg1Wu)B&@ZWuLb4=A9E2Dmgr3%?2WBHJU8OFM$Od z(#VKjkiqaZ857~FEyFzOfc0^#hgQ65u8a7G~tB?iREx|8a2X(m|P3i z#n%#Gc+hySceoEXel$ zRZ0p%hX$}CGr_JyGzN^pZUQv1bF+1twm0YJUori@pL*OYfAHezKHG28WCrL4I@aaH z)MiYzU(vy+e28W_mP`bwhL-Sv4jqHsaDOHKj-Uv_(<+oi6vIrTgD3w{c<_fdnPzvz z5)irf_~5)tr$;WC?CdNN0MJY(xHQW-ocik59C+oMF6h^2xotaxlPOMSI9)eu?!Q?# zo#AZFY@S(O^n~skfBNF{E?Zf$LyK6B>J z-}CNYecP`Az=aoFbk$W?FPF>Za(VXb+0CA-;luYK)zUU1PxANbG*U-$agZ*Ofcm&-r;!$15Vzww(#4j=xW z@BW@^uD)`)SWKoV@!mZbfn>%hkZw@skW}s8l4+tRD;Fvs|`5dqI5PT_SiG>*=-U5g)wlMzwi zQV1O(_L$GP67flDvY4mfb|aZ-OIKu9?P_^a2Cm+>DKcMcVXfm&A%rdLCGjIxL>Zbn z&U7FWih;bSjFweo=0`^=i%u1%1lfi8O02HQ1LM|DYuZ6hhJ*PU*;NW3$ecP~_ZPp$NY@83gR3L`*v+ zk)_xQUC!Kh1nm)cB9y7+J;lXlrrW%bp4OpoAAY#f>2G$rOOavuuqr+tb$V!uSy$wy zK~K?9QWQ!b<(}RNURmxHCbf)WbMrmGx74AW0t-T^BYv;1S0kWfba!2rz>IW9j^%f zC7`Ir(Yf8+K~|J@_o$FW;>-K6VGXC{Eg7=!Ms&equgGW`OU z%YHHM5&Qj@m}kA5#*1k1v@Gj z=<3Z;Fv`CG{)M6V&QQKIk&k}qVhJjIyOEP59^TEuD+@N3Ga39aKGx*`iD0p?$F5wv z`WYLa{+kol5*We>oH%v*x@Szk{59wIXzhY`WyaPmT5D_RW}%fS>Vej)>Fg#9A9&TGUy0aPYwX$6j~c4L5$` zZNKr>MZfccfBJ%+;1f6A@Y}!roA=#)&jsgS_)XvRZ3hnS-`d(jgGh*oo#_w^5uh;> z#K~k*QfrLyPwo&|_Fb6kpZw&Xd-I#$_=Y#!e(No_-gf(W=N);=Fa6TKy?bBw(wD#X zyI;GrvooDeS?rs!%T20gouthi(Z*!L{AVpvA@&@la9EMmkptye0erkC#o}INJDMDm zCe>WL>L8jfm5KvL8wNjsvEa*N817S?jZu-7*!8d{F+|3?0XxPBO{iyJuX3k-G}3`K zJ_1oKgFwVP*De7h10G#xntZvMly8<-4@}ciQSqMF*ibOFvS&VV4>m4M8UE#@@fkpv z7KQWa>-CyQX9Q4sbW7jnNnO+Eob#)ZEsFkn2TO|JHRA9RbtCcPeanSqSRl?<=q8JB zrD=-i+ccMKtPx++_vrFM9+8B)G)Fm|k*iVsy=ls)A|fO0F$ZSqp;#|Q0J38alS316 zuGIHjNYd%j^=ng3G8{uvEFOV@oW0y5v!2K%B1M-)>sg1`r!W%jL_p~yXfD}BxsNF& zJ_DW4Bx6}&d!s>VC}3kcfv}`>F}IE2a}pxS$^*Gk_S6+G^-iHVn9-DQMZQJE#n_0n zVcrJ~RKt#eb$$mEOK6us)FaZKeQ@N8ji-Fc;b*_} z*m+k@mrJ|Y>bnWLZGl;G8Z3HTXkT#E>95&%-bjnTz5R1F4?;pfB#UyXyl@TR@pTId z9uh&aw4hfwZNU6BO#b|ZGt`a;rh|S#>-)@;zii(JfB(eE`?d}r#2wrB{oA&v^cR z+ml^lHv_V%B13?RC*zX{Y~?RurGHXaX1*H1O8P*&?hc*r$-GhS?p#+uFcZw53345& z>pBSPW5dV45V14H670<9&v@oDf8#fP{dKQ<-A6w9k+;6}ZEt<++gu-Gy2~%W{Eh$D z8(;Gs-!k9XnRF8{UPsUiuoAy=ACFm(bLilK>Dt=kpZK`br_X%*cYepi z4?X)gY*Kja{Hqi4?#aac$YHB&;^1(QVL; z4qeI&3lRrodL<|@COo;7EokNib6~_R%HCw4XQ6^)n!l`v@XAad%0FVd8 zkS}+}$uerH_)Z3B$YFQ=6FV@kXzBbj3v71Z@?8?YMC+?z;GMGZEt4(2PLFdG7usc_#cr5 zzayZvkyTonRMWCmN)@s2R}vy(@0zs-#=y+-3_9fj_C{vqtCg;o#IS?i8JDK>0stx{ z5Q)gm-(DK%);1;^duIoZ%#L0>yXMLJ zuYJY_%)pZUf0_I9U|W~$E)ZKQ_de&|ckjDztWh;QRjDMEhSGo}kc1=z2&tikFwlmc zL}TbM(LvbZc0`8*gTrCtrn{r@AGW|42)6^e10(2$h6dbpH()FwkR<^^2sBSc)vNK< zo9}SW{+9p9%#~}*FZa1m*je@N+57u4SFT*ShTmHI8?GL0h)^!a)kf&J0=@vm_;Una z)HvtrHQYJWz}~rcLck5SZqP*1G1ukm%XLP@45Vnq{bCYUc-=q~wzsYNiiV*jwT-z! zY%iTJ+ts7f7yqg!zxvzm{nvl;Q+MxP+<)`>@`>{PFT4u?hr^*9p&Xzb1C=3y?HaZX z@Bjtz^WJ;w`S)D@%uhbLbGUf*{{8KUU-#|zUiikt{Rdk)0ByZS#jELtD_7d2jzS1> z%lf$Bm4^USS|$+rRo7zv2G<`^V$4;XE9kP2M>u z8SwrV2#Cf5oNnK~{hi80=&E!>9UK= z(<-m*w`kE#t;C@Dak>in$5_Ry8oskR9h9@nBg<+720 z>#QPR2)ap|Mj0Il_sAe0h~#gYS>_yMleXn>Mn6}$BSsoQlEy+0fU+vha1Zuw%A57G zfu)3tOqwYfFxz5jS-Paoh3O?I3GlT!EM!pg%yQ!jc1HS>vE>)S6hQBe(MhMq3~g`^ zEsBf*Qf+}9UGiju2j_9=1o?q}4vPDHfxns5zhh)j$7cNVFy#QM+#xI$z; zVe?8QR0W*^OTU^eoS~zg2?P+d5GL39G{mVQOhc(P8_@XyV|Bo&tP{J8pgAtF|36_* zeh%wrJE3OM!q}EUmLw-4*+-q5;wR8cW=>{n(Xyy>tacu`kYmG48l%#j{>YGpr=MmK zxiY3)?QgNRBwq4hbq}3+K4I?YK?uR%4mrDwf5rzmgH=Gk03u+4?TQ{g+{z_<`EPjo zBfsJ4(fb+z0S=NSx_Y$H1FDbNVLUVpd`wE?vt&XmB}8XNd_%F6-;;B>uRK3~4^cRu-pf9JCw z|M@qbe&X)sEqeLmrx$3*0*lM=>*#e9=@@aL;1un zo(OIgyg1SEmDgVT>R>_{lu+}=)wK_$K$v-wBNyKJ>PtFN^Sr|bbWmd0OfFijq3A&4u^6$94pHRp!Gzr z29SbtwmD~##b=9;Z`M58n{y>cvG?+OJx6I3IqfDhD;R~>82-Z)!;&68B5s30P2eF+d z*JN1S1HkUw64!+|Flw@bLlVbJ!t6VJiNVihq}}rrUM}dRprm%QW;D$xgXYIr_6=b+ zH;->6Ao7xQAJkCyHFhap+X1{$+kvx$JYYU#j|B7RwX3Ub%1O0)6TCuB{HZnT3yx%H zPWD#OKdThas(4dYQ0FUm&|R0b_V zZ0}HN`3fi4Mn^y?Nttb6ze}MFIm-SCudxS2YBy!H!!SM+*l*IXE#>n=9lEM^*vHH` zKb!R*D(7&p^kP|2X;5?Zl%O6c#s>#^>S`NiUDJY*Kk5~^vEv@=AUn*3X{XQ|gc0RY zFmk09Q%mwwdxEx>Czs$f(94P)mR-@98UYBfzZ6~AT1^t_i=s9F1Uwvp2xz0L2iE|I z1xo==)ae93v=MCAfIwKVwEIZdIYLZRM_9MUJ6~kdG#gV{Pb`8_uu(F z|I2^z`okxmJl*-T|NBpT?|<}jmru~;#qHzqaJU6{fI~q%p6~(-9-ts?aDCbyJ)o;M z;odIgn-8*ZrgVM{{2$QllPuP#ErIx z4<7=+;dlYO(PXs(0IUZPs@q@))=TBOVppNn_m>j^06&_ClblYwaY_IvR7_fqcl#?X z8%5rWp3}UiwSFwx`i_IuUYpJt!}9BHrU70nrk(vPSwNY#x{2x~roxw#=q6L~Fv+wb zG_*%hV&@~+r;s6FbT&zgW;C=sCHYWu=1?TuJ4QrK!h!VopdS^aYPq(v9JBOW6B?Vu z@@ikv-4GS-y89YBI^RL|0aZRCP9{;`6C{R{JTB)l4wLh0nP}0tgSe3l<>P&z6sfVk zD7UG6s^rLRceur7*BU7aKRmb0gFhI|Ac#j9UjV9k`NqEHvxm%t!@W0?qB9&)yJbuU zFupowpef#c#&3+reGMpc)RArv)sjNfL*!2JTui4D8TqEu^A+*h_~PZi;!N}cnPDZ2 z$#B~>k{bm3>p!rQ=eYn}P#8Aemj~T_gl))2`WHUe(;0(_ja~Pt%p?W}dCAC9#h5?h zZq_FDG9P8qPvXUbS})s1$e9;KI$dv4P;Fr7-T!XooioOm}?_K+kG7fa%b~tRb{J#CF#H zc!{5h7MoM@Yjs$mZ(C@Y5zaQ6L7sL88T~nN$%9f4*{6FG)R{1Ai-#oR^U$bfn!u^Dqay@300EjSFZysS1atu8w?~jD)8wc7z+VBcy#^i|E+hv z{NnvT^PjwUL2tWv@5y6%;(`i1DyK(qdQb=sun{29MyFGIB_RRe0k*P1Io`Q_xO(*P z({FtGZSSSu^#|YcmA~cogV(mhMcctkyZOo0H zhLDetdLkKUaRl*jIMk=`Y}-cLh6u-tW3v+R04wjuG}k)Gfv(bWw=EcLU2(iJUi4#a zi##9GIkRkG8F;G#&Dxgv{kJ&SsM&6yoG_EE%Pe^~&$lk%h7;vy6@Km!%nE8Uk2%|g z#uM3sgYT>d&ZI3*PFA-f%&_7U&IzuLuQALJv65L-9px0ux|J0g5Us-C zxK3!K9zAhmYCiK<={f*s-2=!tmMlliL&$2_=5>t@YW&&<7a_G?5%9)heaH$|+Ea&U zFeGy}@RO%@{q}B6C(8*neGo=uS4TAd)f34NJIf3U6ZzP{sAd0Jj&P{o^zRXJ?M8}~ ztgRQnVg&%JxAJ({eZOTUZ+g`;D_n(isLc~y~8=kBuH>UP#-Y~{kPkYHN9MEA)Rr3 zpw4=vev8)m(cE~wp1Vd3TRszn2UClkC{R`*%PKpbl-YGZBs^k{4-IfN#gKuz$r29q z6vfV@euxBH4>v<*Mkz2_wx~ePMmWyh&Ru}dh%lc$7$O0YS%TTzJithT=7$37si|H_ zgOCHO2KShtVOR!OY8L_^0Eu5?fDvT>ERLg?b`Y*rnidYJ(e6%$k8npIttL`Rg9GNX zucFP$r}zjXRF`#ktpM0`6-+LpQR@e&N~N!wuvRHVp@-V!*w)9B=@M33S1E{C5KI3$ zt_viff&~evP<`$(aKQ@^53pUq>2Ufj|H1pN==2x==!=(cK7G8nph5@010j`mhg*RH z2-^WS+=xyaQLXU6Mh~vQU?{P(R~ekvtp@#N$zrHtS|?T{entl-bBNa9O&ckM08=e+{Kq7c7xz_sJ5I@Tu@6rS%tn7%4XwySm$_+L8k1H4- z35i<a8{d$E8L6usk{ahjKcg>J7}PbBe#EqL9wtXK2>=LInGLHE=6x~S+l@n zhxc3{%w7RU${l^ViUH6o{mCsrm?M>OufG#cfTAP<&2J<*xOssVx^leWxP zxQ-;)VPImP#^_^#A|V{6@!P(?_qx7<6M63dqGEsHW$hw8L+$}o2Z(lL?ij<3V~gPf zh7DQSksWxbQ_&rjGxJ8gcXXFGkN3m0nNP7-_mm9tVpMbcKHmzs7#3|Xk7q_KOvivM zy3})KZgA4h)IimhG%8D2O}B5-+(fA(GJ!B&HRn7!Y*#{lr+g?e#K~zGBb=#S*aKeL zrqjUZ{nvl5V5Dz*1MxY6D^_AeM9p&(%v@`OZ{5Ph?o{?rd;x%Z6IOStK@3=KP0RT0 zz;Xqs{u02>Z2|%H48%t&m>Hp;?#9i-?#X=3w~1>&SVE(NKEem3S%-KiU2vpoSu?RP zGp%{cQQS`D+b~(>gQ$K$G}Ekr9uKe8>N;LiL~`5O3H%zHQ9DHaAYVVwWzw zBB-cx&fsUn;;9I`q`QQ6T-v7?8I6ipeba|`vnohU`rO)-FR_gw+ zUa8-_3c2t0B*>fPh6Fqkt)sN=&Qmz6WKGCkYkCLar?}8C@OMAjdLn?W76wg9$(Jhj z8#84vY}HP}{)8%p+k6_3&p2~6>#XiN#(6X}d86ScfQHjux3bDPn83a;$eNVQEMz4R zCveshG-^Kf+{RK_pz!OHl>{4vOwPji! zidCE*zk5uUVM(n4wlJfcwwDXW9`K?2xqa!E}qx67{w#j5ua12yfM37U+;& zcSEN+!^tWD!2K#-uhtpRXQkTLxC}52OS9UGOMC?U(JjhOJ zoS|o_eMD#Lg`q=Kmww2xygX)zG&A$!i#jV2bS|!lpweS6$(hs9hj>u|*4Bn3S7FG? zyuz%3JB_Jz5cR2=KLtbV-7Zo8qhv|OMf7gQk2Hd)Y>A{Nl8chXY=}Zd{XSHsC+l+6 zwLSsZ-4?kYcc}Ufan&SmORFycZotK5gkGEBA`}6jxJRZ*3o8fXA|kt4-EpOV-}gWJ(6|1c=dLfd2d~rdvOT^TyJH?2GQvQAF0jyEXh0u< ze{UYpM{Eg7NG7(i{ZaQBg}P1dxCe-?Ln>51)@Kyz5(yoV#kaKwC9xb zm9xAA?}6tPt8A98)D{*>0Z0)-b3;tW?DlNeqS~3_uv#(u7L}6+(1xomO>?4jC|-4V zFq7w82l~NwOc0^rvNS(^?NP`E>-xeL=$lqWbe#u5w)Vuug>9mrSxylJQxoJ2t{9P4 zo^{F}I4Cif73}alGC_*6zG_qQaP7;BAY#;E(S=-~)*`u|iZ|mtTk(HO?y*QhstBBK;YKXu1#O@{4yz1PHXT zNDzPkb3ly0#8oPN#id1V;aIA_k=TOcpI91`wY!9V>bp{lEPyU;0CT`IW!#O7a+s|FU{{y$b z{@1<#*Z!ODdj5llH(t9w5FRfIQh%d)Z{4BGKUDR}+_ynZQE(Ii9gK!r03iU_2o3;d zHwIuZaZc_&WA51P?5EkN=~*LE;Sl?;1>O6LbOw2Ug?NNt+DpUgf2f}k;2P5&7spAd z*L=nV{Hm-{N-C9iNwS^NiESd}c?*-bvxdx%pVis-nUot=V%F}!6*i1-mbx#HdZjj8 z5QjBSZJv+GuMvB!n-tQQJ=y6whp!;6gRD8lEXN=c)MjcRILRR87zAMe%|X$?o6|MK zNW+8XIi$Er3%1*2&Pj%Bq!*ByYBkEk4T=XlxU>6{#3RynC(j^6jx#k0)u*`7lQN^J zH81U_xLI9eA`@BUY6dB!I-*BA2y>O3KuvU2c+=m+ip;mHfc!_`~=Oy?*XsVk4nn6ZjI#DmeZ_cO7l$XK4z z3z0fM{EqSC=je9qF*Nu_+}-M=iG`EknU|-mv`+>O=J(EZC~TW3u>?=29ilUyrw$tX zsh8ECI<4oo3PF%O$UC$(sqh3ug`hs-`4YEl*tXNx|Bk1>{&zh6sh>andn`;;{gjIZgp`;g+|>;34ysX z0%#(kc1aSK67(qOvgj3h?@XJmlB(KJ1e$PJfsEmR(!=ye%W4AD7Y4dh{W_eFNS`J% zPAqh2bNh;|j^iAh%rZi#JK$|z|B@2mIYozAJR|XvYN4~5a{_y~gFPsR^reniuOSC-*ogJP8e>oq@)L3>)FwPTn9^Ge z-qvBZBO}Q9F}HHVUljwly-itIFmo7*)zg8AU82=UhP9tnS*8Oln*C|Lcxh{eARej%2k!Xa6tmw z!err22kdr8>z-&=$Q6b~KoSL#eowv%gkZPR=&O{ET!Ttg{3(a-(%=_3SOR(ffnH-m zrY{5nxhEbYnt|GH+5MRol20MX&Lkq=D}f;0v3qCEQ8*$y=QP+SRlYuz-OC&E*X1^$D2yzO2FPJBpMaGc%jHP)y-%qFfcz+e9 zc7A_j=oUK6x4I=$D0nK8SpKRgKq_jx*K=O!*w8re2nfg{ zu4WOCJeLQ_4cBFYT{H^GK%XNrK+OuItX(<7-rnMUSF*E#SJCY8)|kK;ucn^Txz=hk zZf9)}c06rOQq~;ReZEnD!TgwCON_%72GsdPg6^Djlr*-N4{^+D| zc@t2LIC*W8pJ;aW9ML=9Lh^&Icu1ExCWF0I`_Lb=Jw3?022Lbt6D*aE*)vShF+dRs zu81-ir#2%Vhzc1*+BJsE7UJ>z;3j0Vb{9{yU@jDmV>_*;%2=3p;0d6ZLo!?22NOY? z#Oa_`H+*;MWqgKjj);9B|jjSmhlsCrD}}igGwm#iJO|# zvnIY~EFpIdyAFD?BVivi_2P2skmXDaWn`VOG20{8hZqx?QMH>kq6UJYIVt^|2KB34 zzqJObS0{QFy*1Z@DBROfoJOOgN0EJ7B*Y~Tb`s-!&5?uz2mqryun>Y@`DBdQ2oMgn z?t|4sZGcAr!tI(KUTsKJ7lQ2->QD-P_H!@&_>cXwXP$oc%fIYPX(N7Y*oIJmU;{$H z(&rganYw5J`n5T7rIABpf!D<#AQXfXpxhpWfDZO5KFw@_q3xJU6^x`}AoeJx++*fL zf^h1l-vv=CsCN*qObVJq*L0yPjt*7llfYq^VeB4MtCj;n*m(kx7bglw8B7)yaYi>M zrFEwl&ax7ACv8PaeHE;q(8E;stwHhK8Cw5L_F694Csb|a!8Cd%cO-7uE?XNE#BOvZ z63y8Y?7;fGCv5fMMhj>fCJ(DAklgwzi<*^!e8ykCmJyG9ZV<(2qs$Vk_$kiPy*{LV z9%)kGt~GnQSkR*G8t_2h$6-z*1Zly6)%VH{Xk8sqfR`%v-&ED|_g$_@6GY~`oTyH~GK4!$o$5Yt#btEpfrKD~m zwvtzEow3wsX55hSTzQ=DTlZrDJc!l{tPk~=KGl|!&DmEf1#Z2WS-Fqt^He2kLln_Z z9ZTtqV~>nOS&+7yI!0b08y*JuFgKUUAZs8|-%c{2QI9oZ_v`gcOa46Nm|Oi5W46~) z-}+QjW5n@DI61y*GdmjTalF}v4P$XZEC@4FOw>CPQhgGJ?>IXWHSOpur-na*;8*yY zw$SNM6dIg}WOQ%hJV(fuXF&&M-oUYhJ>Ji4sTUGk400v>DKg%+gfP@4 z#y)kc0k@&8q1!Tl>X*C4+qX7-=z>hpJj+cjPBbAnJPeTd%xBX$D8I2|nKSM!Rr?ru zN>QHD#BR8_!#S$MS-w#& z({EO2^#DMGe6ho%b~Bxq^$Kl8|eDn`}V@)UMXC zR7GOqq)OC&dAG7=q#VvC@)=MP*a^&Sw8&DjBYZs69`CKf<9sTkpRwoJ;Nck>_^iYfIgHEX?~klAHlHzTyHOAT$o(RL?wKPQ z@r<7_)?}@rIrK5jCCL&`ALc$Qz@mw^x8G4M%Saz&Kk2C4FSN(zlZ6xI5!8H6T|U8P zS4aSW8`5|wO$2~@UxHd=A_PPNDy7d?fHsEj1pppAeE8zWKXy1A?%lf!0C3paN(qSo zD`N%BMI&6k8P`h6<<#|!h$!CAB$i%dO|TA8mg4@h8;4jk(WEgd)rM%DjC2C>EbFcH zu<{^piF|cX!r_iBI~Os|BqOp#&ALw8uInIo^@<13p^eXdyPU;`Fs3O_Wr5GIiV=t5 zyk^xrW7cQIABLmM6mn8Qx#3A4LnmwRiaA#7lvgVg+Z2RkVgQw3TZ8)okTL0_`Qkv2D`uS` z-U%Ki#h%TDohGrPwrHKM4nNOXG9_3D3@7&ois0pLaJ*bDIW55^Mi1B4|72*SOD4o436s8tv~+9r!wkGgK--G zY#SnCDTPGMoxS4WfTz>x;&>bXwv#VksnPaMUqKoX*K)03dyIy8G(&Aei5r>)RBh)`JUWi@{vsqCCh!L*y`6cZSgA(f@xisy>dgQ3^A(_K?cmA7X@xC;;7SEveAy9pieIB2cvcs zb&~Ci>YG%6dJ2pv{5-!*u`iz@PFvP}xTw3KF7C_%Yi1e|5Xza6mf7RMQbJ)EO=o}0 zT3EST0UKt>(p&(xhFYg^A#ehR-YPB3lbX0?sg|#}nIWY6gN~+(Y{ZcE@PUhPJY|G{ zpOsM$92bS@O`@kG&TT|LGPj}7e=X;H#?e<(tf2gaPtP+YWwsWp*IXA+I?uMGd zQ}_x_JsGXV*c<0#J8KE}7+56>!aP}Pf%PN8({Z#%Hd5|{Nc9FMu2ujLZge8NZV%LI*ZVJd zC{zq~MhFP)rI>6PSPB4a+s3vj`cVC|Ev2=Dh_pd{>P@Lv?1*1n`hxqjncIt=8Z?Jq zSyJL|OYXj~SDu8kYukt*yq|~{Z9K_sG%^Fw4`dA>t#)w}GYmykH+!BqnHWqihV^Qc zis;5eNsF*`BA-@iXD25^op2~_``DeNSvQ^W(#=3`Xzv+LnuI4lz8(ruU-xI&AoZ9Zgf91qbA!ffWDygH<=ec{ zkzs(ar@p-Npz+uIuWyWp?4_TtMm3dkFy}2vvZr1Ksf=_S(F zJ@h7n?jUc_L&gv9f^ib)DQY^q!kkOMvk^8jr(~?ofb;oHigcalMapnqpaO8BTh-gz z*$HzV`go2o`$g5uMw0d*83Nh}dQ}D79~aKN#tM!i9L5B&AOW3DrwEQimG|LLpkD~& z)=~>N0He;JS}CRD!OEg~1{h*1MwQMGr&KgFRSXcntpiAi=`=sWTy=3Q*6tFJ^D#A z2Vqj(0VUamF`Y1nfipHP?Le*&X`!TXT+%1Vd834LMWa>#+XU#ZbzTC4ZVW(lLC_wk zz<;;Ye!cq>D<*K{T=D=?kiBnomYsTtLW`Up~$ zJ5AZ092$^@2#)T@CjgPi&78I5UmV8?hsdPI1L9Uf?@A$ak{g5_GDY`(nO(zV@BJx1 zd(O}JSwoKn-hyaq*{6}z1aI=lclKo7-O}`d1(I`c0#pZkCs|%KKn8ei0d{W5|2MR!Di1%S5GiJ-n1iZ7lRjNwpG;6I6X%z%9*0N&ciq|f9NeM(6=cfUXPq%cHQ|+Llp6oQBd7gM01|Sq~^#j z3ZGXeobi#y#6q;4?EW-OjfaOZ(GOfXl1)^o4ZNbKnk&q!ovDT>U$O3U?R^pIWrkAW`Oj+0DtV{q-FxM#XSfdaG`h=y*}eC0<`|utBM5l367Y2ovuo zd*HMeij98VUlSm0v;iESx_9r+ohN?sXMXahf99vZ{L4S`@X;gA+llDOC!hSwfB7%{ z{@?%mA3S_;_s)~2(+PM}YU!t4Zc_;8Q1I5}#qn_X%x6FIUBBhGeb4v&@n@fV=Fy{v zr|au&qf!o+7Z;!U^e2Dr=Y9qepMLr&0N73^JRlN5A;1C%xb>n9FG4)v*`iclc3U&w z0)o!xTi5IYS<>vOTmm$FD5x#) z5y5!=aK^CB!%BD%UAZSbvOD!|OTww_upg=xhnppYfzms`EDoN_$vVrGygxCYF-s;V zo3Cdyr-ICc)bA0y65wRRf?1K(4!j6xlZy`bECj!JmptTQ}SD)~Ct;BX=as&YS zdVpXfWI+PIcou+gX&EvEu(oWb%h-sx<_v{9$2`bcB2obwsRa5IGh>{ztXovmv20|} zRP5pK@ou8vF+C!kv@$S|KJ)P5X06$yZF2{1sR?hnEP3!k@E8{Qk7dE6svAg6*qeF) zt!&5*Q0|@$jx%D(O^qq2l?SmY+R9xX&vT6A%Gu1Zt&|92TYe1-k9 zsm;oKC?9Hye+&SFt# zXy=%s_dp31iHN;nm4;4}=3CwToWs|AJ~j1p=H6Z&q3H^;DnAB z!emoGOZX;+5Ygdj>Iv{GH6r!XH7lhJpO`xQ#4ZY68M| zVonRi7-R)r{#7U8`s#G+7JSXue9gE1+F$!O{`%kezy66o^@sn%KYZtjC*FMXO#(QS zLn(y-?%uoi_22lb59M&@&K*Q3$0N|T(N=jWr5s8rhjKU^4&?w?5b5<-U;p}F`Hi=3 z-vXlR>(kZM_38Th?%g}vM*r1+`IrB@zxlU5^5q}-`fvEgDjSg%03Z<77afE2V8}Mj z=@)g+O`^hu;`$L$aJhQ0J$iI~?-@kIZ4*zp1c3IwvA#o74B1bk7|`VJx80v&>Le6q z-jG3-v!luINtWU(zq~5SL#DhCW$+`EA0V~RNegqQiG`X|es$rIES)$9E6e@eXYVX0 zGl_}jRG2KiwJAZ!Jgch@xS0_{~eGsHyZ)mom1R4TJa`PQOR_Pc+=%^byQO~o9`u&u04!sXKi5VYK z&tg?%!@Lj5{54Exl2!6n-oQpdDoONJ^^q1J4jMftaToxsJ^FoHggr#o=RpU>j|}p0770Whz*OrP=E-snf`^;0@b6nc9(B|qZ z$TBiZ!&0Wl)sY!%F(7;HsXdDIJcM-QH}6Azf1ZaIJ83wn*J?uQtXb)@OP&ON^*P_o z$uDn;AA|73kA6NCL^GRVm#{{8zeyzu$o^_}1K zxBmP8;gA1`Klb{Yul=Dv_`iAYd*5?)eT7&M%l!xUzwOuk`fvZXZv%jOq(4b#tOgrJ~9)Wrf7a96y@ndmn zM61r|#RC)o!Z%+#U0-i^?plMP)ATUjh#yKjMgwY*r0;i#+T* zcXrTPDF}joO!5sHV?Z*CT2volStc+}XAL;E8yo7j!cic0LMH|gA=?y|oX2O(_adm9 zp0d3{v>BCecCcmQ=K`FN@vNPnN%B2L9f^hB91Cwe18x=8RZFO4_+{KqM11xfH)!mX z7FKGHM`Bd0lD@S6Ob#6zoJs2WAku%$)xOg5Y~c}yj?Zox=rtYOHw-&Zo6NVvB_juU zQCgks+K$(qrgB9q7 zUO!D|+4IO&sz&VkC^pVXpGj48>bup?CdQm(Hf}nH^bKXtnEWiY&WDPyrpO78p}fc8 z*od8|fMeSf!j%tV=-m5ebFgq0y#$_ga6Uysa!C>krVyNn?ffiHCq=qG$}zw}d{)^p z2UikK&xYKfZdL;gvj-`525P(Da3KfJz_(V9J;BbJuPaa?Y?4vhdJ>7l&qWYOO zLU`AqTQus+!VKyt=9$J_od^JMDCPQkBZBYxAAQ%~{=5ILzxci1`)B^lpZ#b5{3rjl z-~QX*_O|EmKYV!o==yLtT;968yf^^VXYmY8EQkPzh@~Kuf&hoZ;cz%0zy@@>KHY!t z@cMMMU7sF3x_Wqh^$&jVAN==!^KZWT^2=ZSRbTV({I1^v0N2-aI356ipK?Y3urUxO z1inK`C{Ht|{x~A&tHNQE{I>v{9>MFM+pv^7_b!Mwl7)W0Uju-UN2x91rd^FWA~wb9 z7yr_05d-Q6QLQE|5gfC6sJ&k{h3V|4CU*ohWqInXTzvvt*9jU)Qhecqb@CzQbp*Du zOtSOy{n6QH=f9X#0lGBf-_-zZhaF}Z_eveX8F8j(&>#6 z?B^+{$5}2Wki#2hwytqpDsKK%&56PrtOZU*P0J`x^!cpVH}f+Uu86KG8tE}M((LJp z30GO7#CPnHPUq_=5DU+&&v%e02qT{DK$p^v*@r;PAld^0JS_G_LAul#TrKNpErcMX zqWFKpj2trYg)2IDgEsYy`)p+1Ed=Nxz36j`Mn3jTTw$MglFO-%p5cf)G2Nt=ksV)? z?LxkfqBr?u@mgJb) zsUAiIY&Ra^27ra0eCF`!kK-G!(%Y{9j{sYv9&m)=M*!d_$nfNZh)7rn0S;ISUThB^ zK79AP-u<8aNB{AiC+_?g|HXg)LqGTre&`4PK`apjgi=a-ce#86R;#>FUD*Z*M0C2| zh)zUG=D*^rzV?s)(LegNzw)aeK74p_ap)}8H*JwvLk%JA0m`*PHI~<3{YO%aP67yk z2oK)aUVr&?=Wco8ZaH0VfJj>hE*+)@?B7xb& zapZelHw7TQ-$gOGuHhjt-tFUsuqKw-hKfG%S5X6!%&5+hS`vx+eptIXt4(Cp5ZwCgC{P zdt{wQ_7xeP=iN_aqefVQm0KZF>)@WitS}st&2CDUsDVMQbP&=JgzGeAFtcRD= zcK7MS?I-b-&tBhu{o;w|;j}RsMBw^RSH%Q^^l67tbkANNPD}*=5FXrr_<8Sn_y6U4 z{@B0v+kX51?LYg|Km5c0==IlL2cqM}@$&Mb(ndtYQc5X@gA@eZeZ+-rU~yo{NiHaCJp^sFqs?W4|0|Oxb4WGx|v8jGOnCPsM?CZrZYg zQ71Kog_0}q?nL2Snc6cabECe&?*tP=3n_czzNTZ2_{>dEU?)SGp7k3Ld5jg@c4L3o zI`2`=s2RiX8@j7J600rp+!X{7Ly>wSGWyp(gF^tDQfj!2Fs>Y5#%`KhJy&pXF$+@K zqRviJoDt2)V|Shh$Cyg8Izi7U1lRsr`0ECS3~+b;n!^=em@wKNCHAQrdu7YYSTKxH z0c$^MUY?5xW|KC!+BsCm$Cr3d+g}4vr*a#k88UcJZcK)^D_pOOCS&OaP=$C~d>%F^ z-AK4ca){}tpgZ?nuhA?xiSZPGm&PDoe7wbtl)yy7E(Q!w~5Y2CF!~Ldu1~Y1m-yw2WLCva?sVD&I@=x&$9D6q7sfy#p~Ghvu{ZYp}k_Dv)DgV zV85y!fdD`SaI2XL91gIZ=<+r^`?iaZ{p{7tpSgbGxeF|H-gXg7pQX7R*6YEWoUQXA zQ!U(J0{|+xL4mSeZFnf({vF@;t-tPDAH4bI_0_2y4y6=4)GKoUKs{Nf+Hco70;tcc zX;`}!bgG1?zTh12)~$=n%ZqKJtE&z100eYGM5tFM`JdQOd`Ivvt1yZno+Uv|=2+n2 z8}QP{PZyWv>9=2OC!(qqh|Mg~V7D=Y$52A7Qo`Q2g_NVK094l~VY^wHI&WbXkPUnttn3mNnQ^p zRynu#8`#&hj&PGkL^vLb6uR_@tC!Jy0$O%>_5~vw^pp?rQ|u^b)NrBCn9+2}vU)*T z1yX58d9(U&zQ%kSZ3Ol30mH3pjnzp)kuaI%KEWDSY7NmW3MlR|Rz6%w9!p?$do4Z$hf|)V;&$q^Ap=IP9bl6Ryzh7?{pHXj+-tH)C7ICg%uw#sx3v zYuF3nR$xH}p*$7a=N=W1Hc$5ITjC?RGGqnm#nQO*4!ntBcr)zV9XYqqGta%{DH-mF z#nq4WVXbHML|za{wh6sWgfO=LJ$zrnT< z0o99m5MkT4(`f@Jhy|!LTM?Tq3+Ll6YSHs<@G_xmt&k27K|ut3=@VD4zI65858Zy^ zNxZ(^@X&qvh*fPx{nFiJu}1t^(s0Y) z4`S#ZYt?teKU>76j&3V=T9_r8te|U`3_7uO`zEy1W-lmy`CKo3No_ZoZBk0NhG%#CR5Fat$3hd0iaf$AwbNq+V{${ zFocQg(Ni<4@(B0^PPpSyVzHC$2&NFcDYBjax*`%3B=ciBM=lx)N2tqc91+PQakVs0tYog|Ta|fxY}Hyy2+lPNN+AC0L^iT8K5tS{i0=`= zFe^h*Su@r(0$^lOs-oA(Lc<=F}KmF*_ zAH9C>lgDy^`cN2Z{5`M z+j}oI*Z?;uB`)Hlt?3xg8Q>=F5dBik`5Op}SWjTiO?yJFRwh~*b(Sw$;m4;{9uLjy z5xPG`+oiY-vrQcLt)`C1Cy?`I(#w@ipLs$ty~j0+{Dj_bj_E^Iz*^2e%oS5jXu=+R zsPoSDRBltKt{y~s1e{C_QoC04K}?;-k+ob=5j56822)K{NC5y}aN?duXyL*0{eW?d zZrbzQi{c;q&eb&{ zaLA@P3>FfM?=7l>^jJH^ZXHVQudFng;l#Np3;~65T2MBnGejonpde310eGA_qQx6b z?iM>%>L4OUb}1=JFU<}XG}|G}0I^>dT|1`7!YO&Czu*A?KxFj6^3N)Tq!QGPai==s z`>eHjBn1Fn)_jcicnkL+s&wQpY16hk_Z4_vJaDpEsJSQqAY|`G zDJT+KPJ5J7HOys9CO0$>5^>ut>KQgd<(cT-WR|c4>j6Xsm2f~fT;Czu3!?Ph53wkW%5X7O5RmUJ2LJi z)a{k}f*%zwHsf>G#IhL!EE2(bFH#AYnLx-==yr0yv0u;$!;n9)nN8>yTkt>maRxS2j0-#Bk>)DWoh-OqIhb(5HNo1LVXDf#f%p72ZIg4RBat9;a83DC`+SxPLNiu57T! z`o6=&2kCe`G_VSWafa#ab}5*a`YQmt(;%Z;XJH@>j%8dKtcnhx{b?j;p_fA?l0NB1 zupg9RQm$e-HXD}uyar^h8GOEE95==f;TUaSQS37qsKhb--2@~Y6DRAYV7=c;_bUh+ zARyq8wi8|6qIZAb_M5NW|L9NMzr6Fr-M8U(sw*6|65uI@$A^g1pyEfC6%Z9Smm+JOX{tA)7sEo_Fp-O%9DehXQA zn?O1mhx@wTDRbK!m|>_#OqZgmNzKunXNj^QP(L?wlb$P5$SBWS9O;jOoT(` zRu^?b`F4NS_-4o^TEsk{fvZ{QT$6bsPcuwcX4&j|u!JOhqx*%%ER6F%!Q@%aDl-kQ z40DH>nM0h;bhEC<~wVY5P7*CBHEFAI}-}o z<%GoldchiplZOT-#l#zjabnqr8{(#e`s&hV)u+>v$R719QUZ~8s_-7e&6V?uKJl;E zW{n9v2pr%f@P(_867c9EixE;e4S41yU{7?7&AP?PlWIlhf}Guu=h+wwcRlUC!oIbH z0c#G}0)b3tnYnl8FLwJ(fQS5iMmEHPb^Xj3PeP@i{=V0h;3Z5>J|WB9yyS=xK&fd# zyV`}{ue;NP!bV#OfLzwm{%wxB`kGO`W|zjZQFDH%lLxgEvQLPWoll=d%1NvZ0w9-q90U5c4dTB5igF4jK-Vz=lGfM`zKK?}{0xGHC>c!~st* zqhhb-7@#K00m#$z#9WC4{qY(th&VLHI%(!g@OM20Jw}_~rYwdtWJ83pVg=BJGeV!1 z5~~cKV%((d+PEhIfKp%sxVqZzJdN-B!dt)a;}3uC#~ys>D{nvX9MY*O3ju=qJx|Zt zg0!DQPKEU!f-?M1Kit^t7x%Plj%brLVt3b+mKk*=>ZK>;tB7AuNJ6KDuR6Xru^b|AZ6pzMBu~2Rq zE)6+~m#Neq^w!7vukoS1Oh7M+r;xJ+9LYg_X(^=9jM>%j)LqE~A!p4C+3yQD4a~Nz zVF1krI&|$O--&~!Pgi>cUF3?LmCH3&!V{!o9L_llXz{33-W96emGP@ zuXqznpWrB^3C0uUltHManLu{PRBpIP9d6sAplSKUsC+;T%@&y@xwW#7g9~Gzn2ZRZ zKLs;JLl#O*+j!zlf8dQ7D>4$kS_iRmvgT_VQBl>Y{}Gc9M=&4u74CId02#ecH_hAI zZc~&agN7vbo4LXr@odZ)TfhghNwKj~c(@tQn57wNg1iHYqDEyL-L>v>I^MrbwlDBF z^|=hbA3glQmtEX@S3v;UTA`(hNHanGk{RJxkyYJX zB}4$&{hIU*Jg{z8?LV9<7k}7->WKOv9%$$c(7yNH{g{T) zrk$mhrdw#>nT~~E-NPEQggFm$;bastZ@kO7IU9W*|0W=$dMvqFT`?6LS~mGeL8N!= zlwq4r9(&qcJJ&d~pV#GJ+5Zl3WM}E=hFk z(KJ)CIBLOW14;V0JlQ^_3?=@a3F%Qgn3%`ad^8F_nPB>wBbWi_zieMn* zY)9H#NA2<{(fiOkhB`^-xXDQ;0uPvQ@nw<-EIW{4K9O;S{O{;?$g>Y>D;%l>5?bDb zjI5Hm%waJu4orv&rh5H}(&{ss^G@SU2~Lcs=NofL6oogrICcy`76 zdCuqQ>LQeq&~^?s&gJKfOku5&Qv#V0g`LeIb6_{$XZk4TVmeceQhX-T*wM-Gc;;|b zFpNY(%Q%jLPo6pcbhHQR(D6EA`xf#&UqF;IEBq`mrKr-^*^CfIrs(NjPA;wi0S@%_ z)5e~a9~a0g#Qk$N1Ym_8M<+I{7}rO56o0j|FQ$CS)PBd8=zE7a*)m-pX@}&hz?&b_ z?|U&@X0Bb;HO%?!v&m4zJz3AK-G;a@qBAVTE0WjhkUfj16XDAoX>6!T5#$v7Vc2K2 zmnQSAKfV!>wyl?2h&I}udB^eM;_$Jbx%$a}{OCQOfBDW2mU4+i1fzTd0DP%Um)@qY zebIiq4ZvE5K`4asX*iv58&29|5|7uH+*BCVJYDZ?K`n0(Krc=K5Ny~=Kvg{Qfyicf zn+~bob%lV4fbj5T_{ASTz5Kb;d%mc=>;1>$t?dMWrMn0KKtd+ha1vC<+~(DZzS`4B z3H?{r4O7{f_|nW?jK+Ec_r!g{jR_NPQuJ^RK&SOagq(`RNX9gWp4Lv)2qvrYQ&Rp z2@4by!xM5_^ z90lnYa?N$#q983$Bixm!>N)qP(q-=@LG!8WX&w)upaqgJUTJJh5`&;4-WJij#FX(n zRztOyy6zE+JTCd!1;%o2H5KV78yg}uFuxORdUAoCs+bCyK>z?aCTuqQ@wa;kc<1E_ zeKM3&_JPoMr!7S#WVvFQ9WqHs)_J0Z6CfZl5RTM}hJYtCNM(?{_jV%YO~5AVZVRo9*)z`=%%^t~A?uMFPlI zyVkF5T(!Y%BLFN|D>ev7Q0R2p?mkyu_|nUd|LoOAfBM18pE4`^JoQ~#n-LIeOT8*K%!P_K9pxO-R|%O`}&_(lZnE$NLE^o1Y*Vte*-52PZ0 z)2-b;MMPZ%x9++HDC*%I3>7@I84JOqm+6zguzl)d+u@R4_;7jl-K8A16OEb<)T@Zt z&tero5-;^Z(;);UXJ=M>eY}4j{22JMfZBb_KvKTG zCM@trox*lN9LPs<($R3hPycMSrhB&G?&d)m_GF!%HTs#wbUkRmxw-dqxt0{5TKI&F zn1Z+7;7J=;R|2{KFpZQ*cs#dhjez?^6E=n&*crrS&rT}c$CX&)bGx0MH|nF*&+CUX zQUez~S$SaMvhsFD6kczcAuwZ+6OigktggP~(LVzyOHrMLrGS|6ty!!G_TgGqa?bfY zlE`kBE{>ZSN&4yzbn!+k1^lV6L4bySICh4it}dfv_Ve`v$vBdg5rRV&U4n;FSsiC3 zbM~P>?@6vgy5f<-sOpoFeYY?{L|L1lxS%uJj*Egbxnx8hzpYe!%;#ydwKFlr3Sdf& z+Vz=K5>nzJ19nDVrt#ydg<$eTKFc%uLxZrY86BzpfNimOhE817He_TKssboK>+7J| zkP5^@BH38)I0TRqD_!FOx|;{F)Rjp?2IN=0770ncLstjQhJ@kptXiz#gwfiBjz!O! z;lOr6YHDzVN=$?X(P17Eyrk*m8;9-e*o#nW#u zckV&C0KCBZBCT4*seTE>d#?;@2mplu5DS!2t0XfIg9iWr+Umwvz6nkM09d-V6h!2& zq0)|3L;t~w*+T|fTJebhd#winXd^(RYq)+$Z@z&qePVm%)7QAcv!8c(+k49GCyDTM zg3|666F{oZMqG>c2_)%eda6Ik)9@*C3=9Wuuyjm|zt!*s}95E;~$?KmirwgiUtJ%q=tL%tOzmdF;q5dN--D ziVY1Rq{Yj#_Df#h;)Sa5Z^vueH?KJUV0+?b!t{Z4r&S?;4;tV2!aSPO2fU! zHY*VKjUjt_>DeSrhBQK$@xF)2OFzX^pgySjh`B(=-iox9`E{-vq zwZ^Gdk2u6^h(1=?IIf_&mpfamUt`D&6ESl1o!dbZ!$=j0rShd(0m13HrDuj9b$bqw zxW~W~DkmBvL!M<*>S#t!M=e!TM4ux7|HiByc(W2>ML~*xG)FVRJ~LW%CjMRSkOUU8 zLkMDaY2K`{w8Hp#Gs+3BVl@Tff5A*E>_r~5uxIW`EBJ86pwuDJ4VH(BPk88NbJ4DneuGlQX$@OLv4K}vP_CBO1IW!ACODmRa@J5YfdZACuIZ`38ekuSP zAogbg6A(f<0&P?->G}5_@4dae`q|T~pWZ(C3y)s>Il6qJ+`V_Wd#@ZXupGuKs2YlG zY>W$EpgI=>Kq&R{oC4+0?!zkBD2UL~VA)nTn-J^l$lo-cQw z!g6^!0U%-7s9m4aXR1yP^!JJOs53d3w%h4`#5Q@FzzpeF-!Li|dG;qkcivofRIUFE zA}P9>&ayi^#0PSm|C&0XK?ek$u_yCUa{CMenasl!BbMr-xcpb2Cxq#@`0A(55UGlo z;l8;Kt4)Go@l&>{#Y~%w8e=!nzQtzDq9;v76i)3@`u0_1ZcdY?dG+q8iLtCS%J0*h z)2u-vNnZouOw7&YqIUK@>jtM_b0P1@YS;5Pr#DmYxxprM;bZZOZ}eLN zgcgZ-E=1mq6J?w)lf=KQ9GH8eJhm=%Ua?tNFx%;(EZXn)Au4r6&T8tFQw zBOV)|9-c9FZnMfCE@2|cun1y0%;Q;|fP33hVP=>JdIH#jo|LMZ^(n29{ zSoSrKPJ!x!N5S1avFrWO{gmf-A}nw7DfBhVXqJrT=;J=f9`mZL4}lEa1@r2B{Y=C? z)ow5&6X(8C;o=TSWrsVcOvBrCH&Q?2@S7Q=klCR31*F%)o7f68-c<_#PR{S}DYHJ= zg@}Z}t=fm~BT9_Z%Ob#SjVTOS=8}wEr~yh{;wrNlI}AbiPzC&%`??&t?FO{bR;y2RDzw3Nf(`KWsJ#Bk?eu7S_IbLzLr@Mx zhZAjZ*nj{_t?3kO3lO2EY@=X~SUMc*{;b_SMzqleumKSOA@v6~p9rAfeZo+9lHIL#BUrvD{fV*795mUb1Y<|Kghj_2ODerxoB*X=? zZz5DNwTNqLn3KlQ%@UCvIyIR_dCp-a(n;@>Nj~H}jD4nOs;Eh>2obw}4OTm&8Pg0= z2-hI4F}rk?V!E+6BK}D>OfE)!uCYMVUqh@hX@V%s8Y32#JX_GmZ!rtOZb17nn)p>G z{#U)8L`42gqLF$?Rb&J{6d%6<}eWOYx$3UG{_OYa7{eNLNBJvk^bS)-yWvb81xKwEt{3~WfW^@3Z8X*iOI_YMWJD^{soB<_8NPwt@}-6zor>A>4qdmX`?X zH8-@u^%Y&-L3~vH^7p;;Gyn9#zwx`@{`p^geErQ0vG(WKL!|%#9&o(m9H3pu(=Mi| zH*MAP<8q#1inR5$@V^C)2e`N_$0Oho2ndj{w41*WYb^<|+D(yGPhI*mMD79cyb>Xs zhZoN6v?>pG$5G`8C}d2Aob0Ck6P{7miA-8(X!^*Kmr?c?aR1GS!;E zdNt#OLma#dpku}uuq|%fyhE12?MS4B$nR9@CN<1%6?Gw4JicR#7?pE zm>55zcQlrpUW+YcWe`w`Pj)y6?qO_n*oM}rnX25R8#oWBh;Zc7Y|WzK>X6@Yg1!DVmfT= zaI6{AP=Bo$GSxQ#od^I4$s5Wvk!UXo-q-I8h-aH|hG!)88<{z{TUxTC#D0uj7qVhC z@}rHNn_L9|%x{A*HX?u~q|^m8ogLvw;zrcd7$E%@GsD^z18y^&Vb3~o>6>bjCKue( zwhYorY>N>aQzeXrR8K8J(#3Fpvx<-38b?R1yC)$LDK{l_v7?9$-TyR)MNq5q003A3 zh_q#*nAT-jAQ5#zRORgp6D5@GWaMnZyrdS&m&aHSv7N>CZDVl(l@V+o$5YQ z9Wi8urzN9Kecw^;yD*|m2-Y8(ERseS_I17;_c?S@2ly>f^(ESn9qyQKcCLacL!zxHrnXnNUh7wTNpkEI|0Y#U&`8>wK~2o695?M3FLPh>92R(q?t8o;2t zF90l~5KWDne)Ui#hCoPA;CkE2wp}6uZE#6&i*Q5OfYkp4BRzIumrz3{ZFdn1zc8H! zf7q5r)E`xbh!wyPV7uz4L&|!hC^e~(Q^1tI;8A@~n^DC`wPN#}DC%yyzS|7OmBSxL z`Arc=@)On${O6FG|LOta#A+YYoL2}s|4d+wy6v*I_u(S;cr;4l>04%IwBlx+8!A}1e5XrNTd4c5UE$0u z&h)FN=LDq-vqNNw%TgFz_?n&kiFz?KN9><9EHoVJ?CXcfM|rUWBMShT2Zd9dr6(!; zS9EVCHQ!89norqqh}%x%)D~wowE0;uhLK+Au`rn25B`vZ3_nm(k5W0q>3mrdoymiW zg#KW>Wvn3G$oFqSS3LU&e~`50=XWk{f&oy1rIK)`9vHX*#_E2{rr@ zT-Jed`w&@~gGXMqzavK+u^KDGNPt*hB3=q?8&G>@O1l#A0JL>GW7j}X>qGJt3`78B zAOeJf)wT*CAZ$QIaMl)5Db*+Sl;)dSNvj|N_PL=T9=B2o0%AEpLD&k?Mz}$r!8pRP z=7B?~*Jd{8MX11iV!C92mF^`uZ+78$860YV?u})J>*|;J4oSW71atIgcF=mB$kCDbcSi{) ziecEVC;M6lE+U)&-;N4|F&L74Y^)W1G9T-wv)u%glwV<9{xdn1wFyKhVBQ(}0%pT$RCXZL13_n8#OXI)FT{VAU3POMmWG&WY(!W}2q)bY zR~F1d3{=l#Jd5GfonZQC3=1^RK`5$crIQa2A;lz53mv@}Lgu(|%D_9D!5VosOS*E& zTbkJ^KH`XD+*eOushjKyYvrQLi8erlb{AH`1KIQk>j3y12(JBYO!vsLIuo=a& zK5|K85v50meyI6;)`?b1>QCxv*lOq#Ns9GI$6`8&p3|sbcBBs)oFz|nwntzk0R-2r zbA`7(PkYV`B!V^N#&yuKCeVP)xIDah6{+@-uWqS@S!THJy==su#q{xCGqkx2E#gyA zHp0QnPI?2=Q!NM9Mpz_|Cj}iGzmU(^Fb6^nH_}UXYBrJR<{wX{&Pz7J+Qstrg&zBC z?-Cj8pq#<8eX@Jg?YLQaP~Z(ag@d+uUDen9ZEYh{U!TE=mHLRTml>N7mvf z?`5;@z%}uX4;;Ew`KWL+5@hi`{oH9S?~rU;SG@A0JolUr)HNxTDcH1dkvhGbwGqrR zeY*iBlS1F`>(=Z;#aV5^x5`Da-!QVBq_okTK4l(r@kq`h+t-MCBPdAs53`M6i4&$3 zlTH|$01_ef`vOMta2#VnnHbL!;^2$}?8(bs&!G95j?8%Be3KaUv%%n4RXBu=8|7WE zJ)R8?JsnZKV=SUVrCXhvRENy4Ov|U-@mM~KP%rC);`eIP$MY$TPODK`o{(lK=NeNu z5yUQ%#zmCQE)h{CfSo5#SDvcww3y@tf{X$=uK5AHB4IpRxNb zz74n2=n?;Z0fbZl;Q;l$SEVjN7AklXyCR9rGH)M%{;G;z02+EYpaWx2qI#oLyN(A5 zkO;OqBbEC0EJ6g@Hh=>jDrf8j+Pm6$_MwbbQ%e!NQM*M-d5N9@V)e<52#xjOD5ur> zR-K^F>*G!A4It7M->BW&rK&Tiejs7LZ?&S2#qRuwtELl&COPzcdOx3K4o0fGD>YMi zpD8_9?-?^0HVG^=arQ~6(>T{H`+2j0Zqk5@Ni^q7-orLg^%%!I&4e*8is1J!FVf11 zbq~=hJO-BmlyflXxpb$|o}1u8n38jc?{bY)Vf}8R>m9yVJOdLXC+pU|mbK1Gik&_A z)mTjqPDBw%N|`-534bBwxW5XaD8ywp;A4u07pYj3eRzsnc#tX>`D-g6Djwop@P(#c3q z$?S_Y|BO<1Hil~t$b)8~WB}ubnPhBuV{}SbrA}gmjCx8{q-{Y*ZQ)ubz%}%}qNiCV zy9d2YMn|HA8@bA~MUvsKidyGN+ONG5-@^uPYuzX+f>O&AZiG7J6unHGyxKeMzNPPN zNBl79pN->X6VM_s^o?;LtEk42(4`I4%c`&?u9v z{E79V9_3mbDWE18ZQYdBY}iGK@Z6vF0KqeRD!rpQf^Iz?6WbmE;WJv8-!4L>Iawc} zN2HmKY=5D@Ia?p!9iDCDq=jIje)HT(6Nu&y1Q_`awS{3=nndo9H^~yZABsbM{9I$Y z>Wp%+OY@UQ{b%LEqh~!NGfcFc36FUu3UV{A)R>qm-s4+}na$50(isjHWL1ZlMmy1K zA7u;_0l2-yoSi##2L|JlKpLoT@7w^PKA*UQ6KwB9fVvw5g#E#Sv;kZL5+Wf$Ih5MF zJ~X-4Z#E#PZ)vX+Au=?rWutnu7jY0fNQZq(#2U8ISH#&^X54;j;mKs zj8;(pH};2#kjKZwi;@GA07?37FhazG0kLwdtnBbYn5@FU%#VTiVj`BtzSyPn471|w zD@4lq)~ZlAoo?tpr|TU)U22!xdn0;JgiN=Ty_w``{EBW>U3`uoo}&1{`KsqYFV<36 z29kn~f*DStR>QRgO=GvMP}m{d$0xdmn&Nn2g#js1vQqY}hZ%Ow+$*A8)Fs2A^Hgf= zB$Q+^C^&d_@)XRLo^WY}e)jh2%z1z_c`j2HpF_d7jg@hDwFM`e`k1QMV??R#o z8e)rjj)wt9s_2xTdfGxGR3S*uU^l`i7#>LZkie;?IW@sj-ZtfstQ?>-E~b`3SYPK3 zIS2M4l}s(a{cCQ8fUFL~sBg=Cjz{J!<1%zv@f$JD%3u=^PGUQwy;jiA| zoK?lhD1(!vb6<>q>^SpXP!8-aob+61vaspt)gm6dc$s*R=Ci?`XR(FOE`v84l=L@p zV`c!6J!GXupxmi17nt=1kpjAuLdVN&^uW$@Ajgx$Ri+E?YY#5JHJzQfX7O|-sXi-# z@aAdgXoaZ?5Lj*-D;!ok#T$&{XQ*)mlpA8AvBQcvA6$pP!`FwI3q)|IVq*~=Z+sQh zAS#qAnsQm>icOiix+G4?pVd`PooR)Hl$~emx8=A#YAku8du|Ht3?(5mffzeF_d~-I zY4>EX7kecTdO&Q_PBIe@yld2wcjx>^oS%W!>As)Ys)Y`{V%lcpWt`*4 zywb>gb=@ZhJ+plh-cflQclL3b!R7wA11rXaQM%*lL`s=Nppg*a?B(2Slx8d)J~2*f z$;QGZKG?DF_*9=dEa)~LSXECZMG@Opce1EsaFs#Z*(pOO6Q8j{nwZDx{Jek(74=^P;<0(dreI-tFT}LxNq?Hzdx!C&Xlvi5A;1 zGq`3~VFL@lRpX)Xo(H^#o zPf7Xd4>Pr&65R2b^BRq8>w&;`o--+jM_DNF?qnoo9Y?X@ddeJSho`6|neZ?I*WDkQ z0BaPr5)ekMzDBNK2MlVmOI|&agiPxKSegwtC}mICjH=HjGHRCUHlkFcNsG-}a%e;; z`x{SgY3#eH-O^Rkx$b5x)iA!SIDv)M=G9v;e7K((5v2v0bhmapivy914FxmqR799T zpLjT*LxA>9Q5I!YhsVt_1S!S2?_{h-msG%fCS9ME$>JWJGlZO?roP8*ZgjuJ-3V_; zE@96{`CxIAsX)DWENgFt4zvm9QA~zHYnU2(wIgY=)^@rLItxH_0)(u&Xl!SYl&amH zz}%)fQoRqtXQlnx6rz4>#fp6Q5Je*e0v9`BFFIC5cdj1{`ZNj|{!IKlK}1ryxdhGq zIlwR$K+lo@+!<$C0WvD+fRL=hol$U&T@qpd?n8dE7S?E2s;69xB`m!xR5XF{Ze}(GPls9T55X-GQfQJJB&W&H^hQAQ~Cbt8&J9#y+p?17FqLY=~P!HaU1 zW(A9N#wcC%{b!`KXB$=%PPEI0>&Bu05&SvbU;DN84ur zDdf-@PIdA;C7(A}Gm<=1hK&-cix!RIVMkVVgkQ2>MF?C4E^XbRQF2Q|27{xc?l(~o zbs~{lV@i-55zvs5R%4K}=!^rI!U&Ww)NqN8^dXZtB8N)}F_plq&VhhqkYUB;zp!dA zY6|Mfk6O5974>pzXccYlHU8Zfuf!v}o zgjmy>q3m7Kl4&;A3H$Z@Y?WuYtXV_%E5VXCbSRbA24pW2U5YDRB3ak!0PxIpyXB@?$u=V8Wt4n65^B)eIgAh4NDIvgqSOFMW|R$W4(JgqF~Xl`dT=YSp}-8M&P zRIzTp1zp;Wpl@gGvU7Hb`k`JV3L>?LA|PtJ4Wo<$gD4CO#{TmTAOdVH*b)H(ltSAH zu;6wLAOG2huYCIYq8w?ve)8#y2QME^56Z1u7cc$N>D6C)_}ZHrTpwS5>9ifL-}fcA zo_zL#PJ{=5b`usHMhTi^Xhrp$GU;deQ939u&k@+#LTwQ1AFGXOB2%e{%`2buMm`Og z4AfNCfU<8H6gU^r`bp0X|B|2R`1qK6VB-%1He^PWIZSWwlVZ@&!C^s7tI&0ZnUhz6 zi({biphPK!km@9ABymkHVn*4`zW#7D0J|^jQ%EnoZVl7S_z85zGaxrQbm{U`m}~j3 zAwhq3-n=UAO3y4O>ygFQoeI~yB9KtXcHeNZ9)BU)#Bf0(Qnx;SmWjB|@>B*$I1W)5 zvUO9D&W|&Bl0(K2R$rNsY?8)-ZwvvQopuMv>b?^XgN1&%uIkccK!b)oXus~dXQ9N%T&&+U7g1TY@03F^_H6W^Dx*#7RkFPjM9sQhvtEUJN=^(_GRwl{7{=^1W< zz^&jGqodhTg%x@a?mEh%UXxfpOps4$IwND#$lEi+{OAY~23pflZp~twI+vYMzM*)O z#yV0l3-<@*snmLnBQUB->9skWGiCmoSHuexREc*I5j*rDCM-_vrx|WR0s1#f$RZ7U zPOe-Tvdc}vq(-0~_ZjJ0Ccq@RRRPC)WQQOe4v9-uu%S=x*P_1S7XfH%oVE9+w^x)? zDTH|Zk-z=fzx@|pynE}3)75r791i7Td;Kor@o#dMOqGfDmxo_{XNsne<@%6fz+%92IsajW)3U7=o-$kxOj7bwCGzz}EVafpxiw zVkYVR9UAN5`=qQO#`Z-y#*Bu@JT`E`X8=vhOy)pj+YSEP$`e}SQ587u3Q11dhR6(yFElmUZ8|xN zU}oB1xpr|%Nhx#bldon-7+%i}E1^nzRg@EXgAJ8a<;HUVxRusTO-aro$g5zglNU(mDT*R7GU*xE^94wB?5sUt3riF@8B7G-;HR>Ce zcl11CI~G-1=Zdsv|z`Z?2xT)ciq;nfePVEB+MxJbsiTVzZt9} zbB@`qn0!hmLVX~Jq`|aMNurLyP=n^p@I?{Lkz&pdpW=Zlv!!H=!my)8UGQGi9QL$z z*uV?*Kv67Hr!1c@RwtKuQ2Yli+?0j}3gHGD6u=Ei!EJ-vPr~Pa_^DgBKKHp7x4V~5 zJUG#z;KlL!c!1YF4e)8)3U1dAuTGD?@GGBw>hmtIuiEWfJqd4bKqoG?H>>J`>e0D< zQGY0i00yi?#Sw8Nc3eIU@PKKEU2TM&1R^bE6q37Vli+<=E z+*j^%MsVcyPBP@!>LVt?33iQN(^q<_Kes%=qxt^T2x{P9IhSYA@iRM{wHsoRJ%Uqh z3590bJ}R9$aPs}bvuCwzQE9?((`Gbad=QnRo6wBv8ZwXdnTQmm1 zNy47qJge;IB`=z>kdk5E?5c=b)_2Cz*0~9q zvp027A?u1%4j4EQ+}SVYFq{CzswGkySwG2cu`XGJ17lZG1er4|zZx)>>do9282>A@ zGc3K6)@Mt!8w<3_^%>)8 zWi+p1H9QCURqNC>iYFxyY%D^Ptn*TKt!)b_fylldId5#fuBO9aWU@@{=-b}}J&(Al z#Zm*n88X=~7cJC2GlFRu6|__<2$Fub?})7FSZ`{I|I!QO%+JfT*(k}Wv10E%CzyUk zw?qF6Fygup)NW=nZt?KHJU~aBhCP8ER#SJkI&3!I>6_5ndZZU;aW5d^pE(%6G-+&Q zLqZ_fu;Ic-kG3!Qx~G2CuYdk;{kaz}ZasMc7kH`z>GI+Lhk}R0>#sfh@Yh~^<99xH zxWGsEPk>ks2#0nZPOrZhzoyou#6Aoadlg>$b-B5{WR+ACQUsao!qTNZCZu`@Lc}Yo zo;ilRyP0ZaEcUq)4jV10R_I3ZQkSA1PO%m#Q^wc{XJn)kkd+v_E04n=mBFI%EaJn=SUpA;jX@-gs3M`I2PP z##zDn*xAfIy3}Py|0lsBJQb?FbQBX4mdsf29r0-`i$%jzM7tO=R!@vRd%;DBQTgE< ztOcGST%ehvuPp94@hubO45Cd%F6&?cgY&g4Qfyy>?=;dbT4u=F7?6k6UHxx{XriO6u|Y7Fb_yvwHozYUj9#_M@F_M|`u`!0dVmO0HLxr49wS#F z@ElFnJE0SfZ=*80%@gvqJBqK$+Yl=U(ukm++`0Gq9qY4o>i?=f@qmofO3On6reAL0 zH&iKqF~Uv#d+!*i(=}s(f;0k6U$7JAMH^T1B#gyH|NDwFL-H_nvl~R zPn*ADG~*#EM!>;#!yK|k(WMOqVJmPr!s#0BJdI!WP0#%3_kZ@4kH2y6iD$1*8*W5p z!$W~1oUX4gpWMFmTc3XJg^SZ^D+M=DYB;Dj1#D-D|4ThbbKpZ0L?AOB+evlAL=#>1 zgm3kI+S%$)8QY+U9;{ z&|B5`fVtE6NHh#+RwYDrBPH(qZn#o~dQGhB!&h}+B1SK*aMWb3OzM?_d(w=*G6_XEWK#2LG&8|ev}{Ke2T3M?{uJw?baKW$BwD!0ym|n7QipO0fHXo7F2D>2{ef4x z1Y`qNojeVzS&b&6wmXXYvyEa=3$=rpxP%DY6sa<36rMRg=j_HLJo%)sxGO-D82FWj zWF-L?K^vb;cw)1Q?$t<}T*Xn;MaF}T8#`0rGFu=+G+7aCWV<)1KCD2E9t9DIuCGpC z@RfJI{2QMBJKy`MZMy~{M1m7s)Ahy0#T&0bde0YMe%aSO0|;~iEO@ANG0>|zpUAYK z-m=xrl0;{In7<5j(67L3S&WpJUUhX3kA)YRPM2TmwQ@vjowGy+MwyM4Dkk&~hNhd# zZDugn^a=5kr!#Fco06gsTw!otjk4ZgvXx$vvM3-iAc?5q?Pw8pIqVTQ9ML(Z%^inP zYCHk)VlBdE^qlDi;y`^m_Qn2R95j@f$OPIN2%a((O2AmkirI#t-%srO`!><`(7t@S z4qB4th?iRsc0D(OkU@K_i0!vo&RCdj$?bH#W}E6UF9AP1u}9FzYRIiuKxU7^*_9Fo zk0JM1&R*5HyT1^rbf``tDS?PDMHA2{j*O0-vG(qiZb`%4)e*7->Yvq^Dj(4{%5V$(5+nT_k{5&YOGIbG zgSPG!-3}r+=OHeKwcf=A;#t11;t#XVKGd!`(e&A`i@8`B{=(^vfsof29RlkSB9c-g zp-jgAPa?u98#Byc`|#LFfOC@z7-3?QcN9X^ybA&V`Dh8TJZW3lZa;{l{D3iSQuj3X zt{&|TywKl-MZ8xsq;+=?L~6Yp`D0vnE)j!5R;q_j7$EX}lZX|~6{tI+yS9D?DyWkAn2pqqX+x3@x&C~Dv(8cMB3KA3`;euy*R0~>dJ~FPX zT|4C5%&I-!&<~+A99A%Y3bZaowwxO4nu7}12zid@IHyhXwE0C7JNPE-Cc7uHZ7OO& zMu2!;9=q*yj&SBK!VZ?yJ~v$e^WhTORb391T(YN!7;2L!7C6t| z2i;dGKt*jg<%6hjy|P8^THH)t-Fo85g7VJ^wBRv|=X2*6pKk0*YpyeVgphN^~!m$4+eT>dBtRTIN z_2`mWN@kU3H4pPb$d|>(ESpUVLr|qyQ(3cF;`54NcJ|Bg57G)o+CM%!Tz8YupJ{dE zc-xG5$~oMAh_PiN%(I&ha+I7j>%3c136CoSVYVFosb`xkP#s)hubwjpto%W2y&ovz z^sZ@z*cD_zSu=E^9SC8ZS3rSJ_OCeCV^XcZP;CR>)xp3OB0 z@Do(wLaB5DL0TdNxf53w3zTDSqr(VDNK{`Mh4uHLcA~Gwa%SZ=MBRL zl(tktsJHdg7kdWv^;sPV4GdV)ej z)M~mYa6*~fqb0lT$I-u2=F)+5*X^(2Gnxyt&K`(LFKGHt)P8P0zU|@xKV7yFVdlJx zaix;mQN5E)&c|?8Y1S$i;^PIt5cio$3w9+@(FFTtGTcXKpo9|CXSUMjuZ&B*Y#l$% zb~wA%LkKMAnIx>!n3!;g#}a}{)w_InizWffM(WOknA#zcBicF7{T*-m_26$sb`5tv&kvl0Lj0stZ7 zvdo$E$b!f5yqJaT0G zjYpx@O*x$I2<;Lf?FOu_fvPh)r<;Y--=dSCezUf|sQFi`)w*jvy24KgB6kN(PmQ1= zVKyU9KgN5{fFFqm?Rw=UGz#8ESL+1xGv^%US!E0}?G*Cab_@{#kTxLPt{%bVo$`@y zdHSFK{nzh*=IVI3g;=hyuZid*-}Ll*zWVa&{s|CHHIxt_Ar%6E{_b_XBYZWbaBXNd zS#XQ}Y9)VNBMo^pafJa=E*AD|ZBJWB5ca-VO(gM|H`R%c^$Fy9n?SS)L7yj2T4+=H z2;~8NH)hps=19H>$2Ca05pDxLW%R6k>>z~I)rYY0(VMGBNK%FNl*DVM^iGwOg%puG z6U7%vnX%z?bBAAfjyreH+4d(BAVdVxCfl{MP-p;OwRVN^Y*!TF*WGAxE`H8pqKs~Hz%P$Yt11C;d1PfT zGQtxdGT~}L8*T7)0X?2Bv`0zPxS9E`X3njWuOFw$i$tojPa4G*8JHh5J(OEnQ|6^$ zus&lK=JFA)LUl?(EHvJ873*^c=eF8dDHG3__+mPX z8KQ%ld&e^w{so)TC`b?su?5OLlt1c1$MPNM;D`I3QejwyELlD>lxpy8=*YkVLKR61 zbI$F&P4efgGr8Ykl!NDOb(nq~Vaph1q-_uw0Le-*p8+8=J_6C!`k5}s`{xu2&n89te7_ml_3#)RcM zDJRG(qB>-Gg2MwK{M14Q5(bdrur&+bt&Je-A!XOqLA+u$!@Mi34vI7R#D+ioPOhc9u0E7r=2%u*xHP{bAtznsq^=shRouGl zKt^`EK)E`z*l-D$->Y%X&W-~UwwslIzjikj>8ItR9L}=7SG9 zXN60DX5vLBQ%q(Ifha`rrh3ZyA$|fw?SMOvjR)v@D!8DoDsFX+m*ZqDS@6xL#`Y}k_OyOyb_7j<%y!RqT~ z2%{f)9>0w=6oL1HIDj7R{00EHiylNz1$M%~XU%!g=vh^1rmx0_xL8TOpy8*|q&X;~b}sO|c+ zjQ=J>M|X>Jb(o_*lRO?q zC_XY#r``Cwz#iza6h3gu)W`q;_2n{#4POWo6It{nDcg`LsH>6F7N-m#)|n)*9+Nbt zrNJk|C)}KOyq%n!rOZqwXmiHl{)|d-4J!Z;UfQn)_v7Lo!PU+5&tNv39!Zy|HGmj1 zElHt!&sm={%LJXUbOf>|c$rCG>6teij!JA|ZI?bLF43)uoP1WVc?+%%hF)nMfFsgKJt z8-54J30otngQ7Ut+C~5bh=gV9=gLrb^<*2) zs<(m#0AQ=S)71Y9zwXJe_@<|S=&#>@^bp_of#X+y>pi>xd`NIWs2Av9yPh64{)Z5O z+9R07qKy#b!s^lkIM!Z-C2&bS>VbK!F?A(DPE}U1oiL6#qH)y?R9cwhi)&o8v}st#<33tqf4SGp=^<6ePyr86p&Fhq(314uPJ8 z{IhOLRqvaXA{jHwL^9?`wpj+8d-lT4pe1gi+r%5!p=K3w7C&}nIpK^_0pu>r&1Dqb zXIK3-w;kHW*qihUBr23~4}>c2`%E7Q#9n~@Fs^fDmi&{6`Uz$%57s?E&?v~tvk;D* z=uGBz6r&75%uFyg&r?9Xa^-=+wVrK`1DyzOy!Yysh{4JJ3V`1XzEi-fjJ5xjJNO zh@1gc(qkC(SkqDoV@V9_Iiu^Hcn&)pBE{8|4LBuzFdUqDUU5WE(H*OH!O|FFcp5Ui zdob*_o-&0Qo$8>6505^H#JO|KhD@~A#Kkc5?oK2Xl$if0&>G?^GM}yvynhx4XE+Jg z9M(CFu$LkLdcC^GMgV|u`5=sxxM?u7>t(ja_5u0}`>4p(fw~E+4Fdou%|rna4{&h+ zDu93+!NrsKRp0*1kADA~pZfS~zu{kf``f?d_Vv|PF3LuL8z2_i7?lq|4FFi66wrGM zMyklu0+8q^01Lr}Ehb}skdG!r)CD3gU^07??x=`c8qEx?>@hmlbKeAjYeOdRG2&;n zk1>IXZ<%BQ{-9*otYzLhIFKk)%@y?w@r+`ur$#Xw)UiqH>`sNw#J7pq=Tfa*`tZ7N ziyP4n=KeH`iuxHaiV7{w4#;0+T`=4`KfKVwRu)uZZw!aO4Xkx>j zt#$!eKM78@yD6gN-wcHSOPKP6I_^7bPU>c!hY7R#OW4!XTp1XS^=EHJjR!5p%Bau- z-ZPkP{_`Skk2sgLm7)2&^mLvaDv2avk&6H)DAazO7h{!trfX46OJ;XBt!2%+<{Gl6 zmBVRF(zi(nK;63?F*#8-3dXjP{u;6#R%FHp@amC?@TWbW!S^2k2Eez6pz(kqr>09Q}FWU*B^O@EJuT5f>%w+^cQ6F{kE`;m2cTyJfL+l|aJHOi zQVr!`V!+WDB?dRK`$~i;wXj2!L;R#WX_XVvL=-k{gWy*H9`CC{ey;UWiyZ57Gf7O! za)0Y1)b7!r_s;y2{J2P`Rxa4B^}prNdlSG(h9?ne+GRLcuhxNDq$U_6$FnVQBN&4b z5TPKHLQtTTf&@^2t_d#jLm#>G;jh1U_hXN~>es)mT;TN^8y19XAOau+f(;1>uv7yF ztWVfE^h;(6h?xNRS|I}YSf0vxKiyb56o4ZF6xygh^aruNQme~SijG$^;&1DQ1pVH! zz|%VyuWSp&>tHeq4Zq6b%iOi1``yyp<0SEzcnnjf@x#N|oULWjt&*%~y0U}8Gd!=J z+FFg503wEgPx-^dg8*Sbp1-k+*MUGy9c?HFv58A*bOdE9%c z)SneoBI*!>;Xd|-OKDjAk>y2w8|`sEZ=AYL{*CTgB?onlgVCj+%{~9Q8E+Qm=G1me z150c;B4N~KitR6d_Hgw^iK|!3#6c+?@DlLw(5H*uBN>+^oA+AXvWV$wLo74q;l<)`zI!AxjgT+{KMD1{rE|RW}`0WaS}cG+c60PmQlc^56^%OD2gr z=!sH{i7v?wezAy(j_7Rkp>3s193(-#S)@<4BWxPZ=?R4!^igV#X0r8JaX6XN(_fY5 z3--nb7cM2w0h3^@Z@?{3a`Obuqkq=mN>>hjRDX>|jk&uz)(>8?#wEqg`R8Ev$ei1L z=ae(tD3}U#TVRQJkHU#EpWtyCa}F|;Xny86*}@S+8q(7==GGa4Ff6f_ri#bjGnFGu zw?VKC06;Z)=nRF!b|JWwaHqt?SXb1%kJFm2XNXlZTKOB1&4cg9WX6g=G;PaNqP4%mBU#hrZcWNAW+Cl0P2dyd_T7W1bDI zS-)@=`N4H-MG2H{1Zti|1W1I3f&kkicVuM=%svqS;_+6Vvy!_&?eCoX)qR+kf z@THHR9$p`|6K>bI(FUi=a(it$7Agfw!BP+k0z$zSj=BW0Ov&P5NX6JximKGNZ3huav5!A2Dyj*$p2~q*^Q^3O{SP@!5D@35F(3TxOy} zo=r{1XSWL~e_xokk7aj#G=N$TBS32N)17mz6~9&wB<YZu?-U77LbJByl|CY&4LMsOtc$70DPTrfM>0W_RlFuV z+Q(oivOn8mE69ntVvVi)YAaSW_AZxz>|@9X7hk^`|EPG*=zAxa1(mEn6ZECctjd$w zwaT3GSl&JeanQo3P^4j>u+BR1MOI28N(>CB`=C=Q*yE~xor@*piB{nF=r`buX;z18d?Bw=cySE*4 zwoha&%bz0mq{)aNa3&@PPW;$zt*0KYbM`4@Ob8PL&!mKL!CXGUSh4Os@L|>>fA%0y z>DG{c)6m)0c&^&9=92N)mJ zvc$mzP(P2-T9e~?Z-C*q zyVqG13>!gza73hx5ZhJO1$;D`g7rC{!9j_tP=`&B#=sAVS_t-fP8ZVm28II9Brnm6 zoTQ#y;;?fyj|Y>jU$+f4N`DARSj^kmlOHCLQgSFoD*H=UvQAL0B0xr>=J)SEUE#QbsQWJexnNlY|pBWwd6Bs8ea7|&9fIlWAJi=8FP$%gD~V%R^E zSWo$(Bg_`MHHT--IH0URxeA$x64hYCV{H9-w%t?Zj9E1TnWSMUHSNmH6c%BIyu~}R z_hh{g)Xh70CL}*Hj_ZKND1k*AK?8zi0uAA|?(pUI=B)x+*oR)if6^&S0k=A)W;oP3O zr2$3i(jp6fy)K!ZA%{tcYCbQxT92VG69;i{hpsN#K;}jOY)(31lqm+&$qs^v47*4s z_K$R8t5N1(Qk&t5a|xZ%ISNDit6_|)I@EUq$r@gD6=w^qyB_m`b0TWRpjzO3oV?ws zDge|3B`%$M<83)v=SV6w|24G?V~Xx_ zhG0Wmu7IufAj{h(32js#xRYu&n%L%a*Tq`f2%Ab5g3^?!+_6F_|5SPR7vY_^Rc$95Hfu?Mxk=zJv`JR{OzR*tZV&*Cca5@5lyL4b zvd&vqm&~{5QS88__ZysN3(v$g7v^r0$vN3d+}6FW2YfqwXV`;8IC5z1uD=HI@9~QC zk=BA=|I-#qo0T*gfNI`;u>GAxOy(1j^E6tbRj=Tp$g4J*xBoNy-ZnSo}7vX!;-RqFq>I;H6m0$3o~r7 z92!I;jL{;EP)gzIAd+pC!^^W1f{{Ct@>frISWsZhB%p^cnRDdt2XHl>LC0~69lF2b z&Kdtc+Ac;PCd)1%7B7p4Y7Q$1nrpSBkL5*ad=2^%mK~{1>@jzYB$tr6Wrow!*(X^Q z)3>8mJpXXQorHXii1p7L-*X2s1ig$T2dk!#q4&7uKGQH1=5f1y%!W?rGjdpxE!W$m zBXE7jbTS^~T0sEzt%mJMnH*EZ@%Cs6ai-n>poVA5IqHcJpj{}0yFar460n6ie!VF6 zm1MOs)C2LOoID#2k$i-<-CB1oL{4nl&GGifbe5Y;SNQ81s*5L`R9hw}(3nRMhQ;Z} zT5onxfCUvQnUlRqSCXh;>g1_I=`7CRiESHfp+_YzF*w;)B*appZbI zE@D&#tz6U=2i`%Duk=}TXUidP6`aP8c=EK?V2-&f& z^5=cO9i`cQh&7+?1!W&UDx6}e=W<&muxr026Nq|-HU!~8FB0hcm`|N@x4U*e zia}0d4*Q!p*I}uUD{WAdW1Kx3ik!YpoM$NRn>D&=*xex;;m?H)ecU!*3Hp-igig?p z9q%xcx(j{NXpCH5rMX&1{%}X)F4-Q$WL|GcRc5$SMM_0__l@k2v@1<)8-L*K#NDk8 zi`+@&Ti5`JJMsaj-w#dBHl*LmJfMT@shjX)H64G%<3c0*eec7+2F#=+GCa@u{w^4x zA$g8slo{o#PF`FK39V(j9Plg4AA8g+LpQLUw^#8afoLg>dX|C@0`jIzfF{&Jl?*!nk9# z?@4DrZ~xW@1L1-0JcsXp3ZHr&KKc{ePyNXD!t3Q7A3DGh zwrhZX=U0E5VERaMbid$Hauui;s}^GPooH)OO5c`f*Pd9qSEq?Vg-`im0{ql$M^H`Z z$u457X|MnpWkjAxT689Fu}kLY*^-{%3Yr+}%gz*CM$R75m)ndt6_Z-Zk&Cs|7X7N` zN-a^I<%Rk!nq*>v2kh?FUt>7B>om1e=HYfy%b94UKo9uXazy|bmn|Sayj(yc1x_?$ zla^xXx%^FlW>+AOOctmZ6<;|bljuN7vCVr~*Rnpc#k`pVaYqJ`-b#4=&J^lQ)GTAU zvtjnAMG!I9sHY@|rjZn#=cuef3?X(N+vo`PhDA#_ph=~!&m+}@B?iU76IJ`_sSQuj z=JBNXB26MfOHj;}XT|COR)2Yo;did05YS0NvSMhsy+`5Ee7z(S=!&@C~)U8r@G#L^Sr4heVFf{*KkVjORdkCjE+Jf|L0x zp&=@Afp#4xpijNZ1ZLzuvn%1u{|i*lD>Mpi$ME%xIUB(;oZnt)UM47!cAj5$UN&)2OF+7D&DF!;3ni?ZHBH5#NDGkkM`_+BFb7wo?pY?>u*iYGxCP z*f+s~Y&|rbbrvyMu9J-l7Myk-!&>6nX(ll%c5CN}B58qt~~e|0nl9{juwJf9UoLUv_!CjoYb# zSOkF5>MLrM8iI03WcV}ja=p=i6XB+NG%RiGMuhbRReW_+?OY2zRToNoWjK1jPXZ!I z`=WtgxH#8cYIV|84nlU75OyD4fC#p0!XrHUe!RR}e*Q;KKmX6KvB29uc)&umwfkTh zy=VIdku6LM7TKvl-8i(d7o11WtkyGW5*YO(gB0l1Wl=-p*Le1ftFWYEhGzEdok{0` zIR{~d=^pvQjX{kGSGA{K5F1L42wPV_JYB5mtiLXXD%I@<4qQ&^Ama^S*iK{RANU!x z1L7waS^>zgu%nYkPr^Pqhe`u|jqBcm^W@V>{+7tVyO0ord>x zxU4Cl2zY(qDg%g|y_w1DerVyt4{2A$?pSc0tW=kOCN{ ze^~Ab8MfTS367PO9?PG1V7LJ=>uxfXt^OF;{1R!V>9W_=rV@M_Qa+O)uaSi~$+m75 zVQ!oW?zG3GX?!yO!I?f8jnpn70pB8jGOx$ASL^6DY8^pIF<}rBmqP*w#RHuUi{~%+ zK9{UYqkOPmDBaOa5s}FCY4dU4N@F^wOLYh19u=vNj`T=jj9$40Ep%pP7*bY z*j3M%QpMfPnGftEryZ6P<)rM{IaTqkR|$Aen=N&IljR~>B|Apjom z`aXT^Cm+7_>Ff7=_}2SBeCu!lrzwaJ4P6+*7ShXnBIy7Qq)y<5!5JEuP zMsy8Vh=@=QbSQ`g;Q&Ce5p7qrU0116IY2oeuyg@&>n|tY>O(c^gT{I$SDFmiQ4IAh zvJ3*icf1#9_ryxfjYu01(%t9FhrR}X_D8RO;U}*T7xdiw52yNGcU4G&tgWc-w5hS$ zp{Y2o%U(^;>j|XKvbJr@qOvddc-Sp}JZril{8=xIr2fJG)bF`yMy1j!<%C7h5%vLA z6uX#j-q}2aNJv0(&M}(Vh|#}HG7V1KHmJ{$i83xjL6OYX<%p!YT+|mrPVC&a1M#~% zxjJSPnAw;TC&QhuC}H1;amTwuDe9=9*uSoXOFvwu$6;zQK_ij~3Pouj@>r8fESDr2 zZL5?MYFQ1pp0gj4l0=wP8*nno00n~v@!4={yjp~qDtN&Wlk=0O;f+iyn0qTAyPg~fJY19AyEuH&?s`i zi7{#u`^<76mx%=301P?D-BtZ*pTN9J$r)n&FXGbuSz&@8u{TrWUXtfN-5end#gof) z`4*WEYgTNfE&jE^QFs@%8#YN4+zM@kP})S68NURv7VwGz%q44)UveoUR+WBTI*VS* zFq<7fxd~}1asi0R#XdsG!vHsr;@*9E-OECQmvGo-ktJdXgyCh;>g<+qH^KvrBQUm3 zDAR-oYCxp+G0)a-BgoK3pv zj?P1c(3!dxiLzFj?-~27khO>(lAqB_)?saC*GHy_3eLCTaqK5c6yK^Qfscwv&`T2X zAiI)w4EwhxM(Ch#^a9mo%xfU~n3Ruj?Sndt`vv|KUFI$bp_LpE8)^jrB*KDtf=~bA z^`|~|{f_tFdjE%SA1>f@O;7-~_E0c_@pLe%ipmtUL~EwhSBC&3Zg}V$aOevz_0k&J zu3>vbgj8>lCOXs!3JbF?PjLmGh5tn|A z2%_y$gGo7dr6=T1?g-PIjI#5eB_$2L`)uO#L^$hO;WcfJUwvgK z{Azd~!OIE9XWEOL*IE5G-^6DugfqWh5Il!#_fkbf$6z>bW^ta)VYw@=X=RH2k$WTpJTUKWez)EA6e#{)~!n z0Sl%91q{PPl&xJpTw;RJ78j&#^w`Pm3*FUFp}p6Nxg5#ahz4R2_7NhP2eb_5<(L)o z<59u^kvuqhu$$C#_<}7uhW*1?G6WVDp#?DY3sjctm&`|8);~UHQDkCl9tWuVmGh!0 zqThW@nNR6B0FkwzD^mK(D^2g1HKTZ4zEC}`F%3N<*VW|^5E~#uyyLJF=KeQOfNi7k3>^fj7Xyg6Fc4XPluF~Kub-R(=%qHk;DaCaj?{m1B_N_FpU3xp;qhnx$@Ry7 zZu`L916}}C=_y0uDda;{Y$))aBy&MN;fcJ&7$V@~P?@?6TXu{pEZ!e-z21R`>`BJV zPg4n^fM-2KF&5TbOeujEAz0<98Hw72P;jxi;YtFX|G4pXgfjGBg}Vjqbd7sw?^o3W zx6St9>s$mWJD2An+Ni)kC(Qeywu7lDm~Q4&)ERl1^8|a>t9^z(VBKB83hMmW$T!5H7z)R@>6Lg%6tuaa;np~dij$L$l_{b=x zIKit5L{il#G3Q4cz8HnwU66+XArU=1Y;wsSn<##Iw@m~BHSgR@*8>uj)W7U^hXZ z+4o7Da(iu`xg#eH!|3kIc6~ULi?M>(3#pS9Mibt%Arm<}3?Bq;KgKoe(Zd0}pmqb( zOcYaWX-3Vbp2rc{>ePe2%z7cK<#S&%qNTyos3?^Kq9THeYTF{45E12 zCUc+%7479_5L<}45>F&UuZN+0+)m^-F58}^4ZBg(sxKXZnN}V*tkJaFO@1w$AdDgC zJy@yyanvadDQ|^fcbf&KyqqL-Q1TgMWFr9aV5zYk(pV8Q(`6D&b{rqv8PX$}gZ_jU zi=;&S^pHeideWx^1!{z;2S}~~Z~gUE1y1+j#b3C3_~7)ymtEevi_|X?d|D%Wh><8}u^#AAKM}P9cr(S&XrB6P3@Q5C@ z!5aY{5uSVopM7U}{<+KdfAG!+zVh;mzwXWlzv{`u)8*lVZF>ZC04xMRbV7i3j}^&v zYtyXk3`pPpNGqn{z({z>E_Na)M9;sky!ul4%tyCp-gbD~2as=G17R+aZ5qjGLY%?n$ea5J`8MkD5=I+rDnVbxU zqqwr8pJFu|z+RVcmRiWBN!}PwnW;n0vo(pW_L0ki|B1oQZJEoLjuY$;yX>>dH(o3) zVSigQ3NreLgZ4?K*jQJ*^9kc$-LtW?&*BZg*H%T9^wJlA56ocRR*ApefB><)@)`K_i&xLSHo&KgiKW$4dk#AN_|9fAFuq z@{j(`{eSwyZ+!O6>pOsNf9CkXFTeM$7oK?T^KRX_SIPlSCptZV&%Su|^FRLj&;96w zfBXY4-}$fc3%~5*%f9N)*M8g6U;6D&!c+L>>(_-4u|R!T5Bn@aC}cKLs#o+wCGk#o zN`0l;L2lP@xPW(k{^6BR!l!>}d*+=5FUz)3IdqwIb>=dM0S*4A&c4;N2b!>#S_ymo zM<(e5e68O?nal>Uzmdc7*!^U{XQr+~T0`F^Xf;}CJl8~|1pTTl(SbBU z6j+Gk?C6jbGh{^e@E1+A?I#ooz0EA0p;R8+>xDz4Y0>ss@K(1^c&ESynfHFx8+H{+ z4OXm-x0pK!;kY;w*_I{grWh1hx9hX@DoU5cBXs6B)IR2tr^=+wuvXJC{&uJ>qi+`! z5M-AqsOdD*T<)RXy#;#fC0%V(Fwt(sjaf!M*WB%UYZV=;u7)983YHNax015O~L+sfS5Fb~{;{bI@lLzcjEvH;0IJA3GRA${IL|J`xU}6AY~& zTtX(L-PF%aI$!4Sp7kKV$ZuhF@~z^{aRsx$1{)A%_e=3xdG(yg=a_B>Hw5A)tdOP> zi(G!nrWmVFlljAxjlYvKic=|zV!ELD*euzPhxkqlTR5W_apji7A<)ki2il~YIx-J7 zk#Eh@5B-o*OHdy7(MD3AuK{h%crqeGPLtJ5T8o^E`^bKi!x{pNw-*hHy?;Lu?1&)I z;TY&0nFS+6GwQ`VoBQ#SNjn<9!#0%d%Z}QcP{@ zL|KEFtdJ75PK1a^NU)6_-Mcn^56!6^FU!MEpZ@k=c=_-Ag;)RC-+S}HX?x%M?)>`i zdgeo4bNk(2bn*OqFQ0twc>7*C9I+t526TcqUfEvy#8aPn@#^RQ;lm&N8?XHE55D?O ze(=qI`gb3E?f1X=E#LjjyT1C?n-5QLY9$o{D*Z~Rji5f8SvCp;+uv?BfMKiUkX8s@ zIT36pxbrl;{k`QAzi@ix)9~C2<@%A`Xx&0ZH31zI0jt~6>RG}q96CeHFm5n~DTM_5 z<;^1@GsSKTvp7!jh2f^NJ0{s5wy{y4*0Yxha|E-L^oysQFM?2ak>trUp0Ple-rq;rp;?NlLCr4 zF8?*>vw#v69msXCfqt$q*(s*LA9ZAGU%QIhU>6?jr>s4OGn)@`PlaRTxj@flhx^&p zWD8Hxc1?>R_;Sne8tpRYw}nduT_Aq?P)c+`8Sm`$_Vwe=tSL7c>z}HmsgP zv})76wwy6mt*#IzIyy;CUI5XjT*p$S6rmZ6X_>0J`vMd)m#A3z9}HC*QM}?h#?{cm%k^^ zF0WIZR`t+t^4?W58nPWq$_)1RHjUmOC9GCA0GB>v?7+k=Ac8}|S6`u*KXdievxldj zKLXKs_4v5>r`^qj?KZW3SkOj7mSOc0wNrn>3<3abv;pD)a056%f$~#7@W$Wx&tCfb z|IO>KzI6T7U;pek|JJ)-@GEbB-WS|@^4;~p?Q}ZPcG`$)Q3ep+X;I5BI+Rdq4M2e(d#s_7hh>^$)l2{GUGik>7sn>VoJI-~|C7P-PwZr9zEk z_2MxWRli@WEo$gSeGD0Oh3NZLSB|u8bUfnI@4!#|!uHaO^!$5DL7)u~`TpP`sRTwc8TMP1pt39wC7Ae_mZ(XY8?S)8PW@vm`(X*(|8em5E`KQ-j_$z0z4^mk7Zz zAGaB}V%Vc)QKYfy=xpaT2FoXetcSw%t@7R{Sa`o6!7}ZI4TV-uvBB~>Nd5H4 z2aW5)u9Fg6PbLMh^*f7O%sWpLa36Yg*~S%QTxWEaYITXPG?Yle>5q3iMY9JqeqLsS zW!z!5##+7iu0r^I{X2eCwgy^AmBX4~yP1m^?g}eTD@bTXz{d(=pR}a%NG9B~TW0rJ zkC%exI?Zk4_tx?eH3fs`y@WzZhON1wPd%hgA&YlBN^;_ssU#pZU+AfK^&Ck=Clzok zTrqt_&NU?9J#7|$J7olG8Y(N%JdEc!YW%8d=f?KDnJ zW>v9D&oaB+g223wpTf6nf74McQa!KP2&qNG_bC_dFbugPpuFkK&+9P4!RM%E&1t(9 zIb|P!+$fny@COH~c8bu^I{jYA$mgvfVQa#1-ZF6C&yHLI08rn_JSQ(i!B}YL$q{;L z)zka0fXXB+^)(=O^U=~wDiAbsmKIS~8nD*(GJ!K%ngmY2sw2(6wfl-zm%+}n4bgWV z?dGOTBYK*$;V>XI&ZUA!+qI9NzFflBK6m}_&Fjy5;qu~%aypHh$Jk-<gx6ht`H7L1puKi=e7ET02{yH3s`f*2aBei zdi477_!QFhwjBy`$d(wh8PgD=Gj{F?@#aUrKsev)U8P)*osP!OgZpkXYi2E-e+d+3 z!_5*5+Uqb>5pfCTjI5~C1ifaQjk(fS6!}>eTjIB8etwUBu*S3YHJF0TB^zm6$0av@ za~)f5#8{3mBEa_e@Y%C<1&3z`qcQj6{X}V5S-Ow!406H2DXyT%XM^J4II_q5c`d9IG+r^_R-WZDRJ6>S@F`|wuIllW4KZWzm7S#2 zwR5AJ=1@+N5#d~NM*$}DNaA~GTI+Mpn&h3oG4->g6!^&u`RO_4Oixgk!3Q_nPGf5+ z8*g14oYRka*mHbU+#u^jvd)D8)afS}VW`tO!!vW~%%~33f#o<{d4ITtTwVPcSuVSj zzW#>2ESEQEySi)Ynw7CA?whM%fOB33WSZ4jrO>&eb}-+2=YN^cR@jaTx7M5M@-_z; zPW?E>AO^pp$i48V>`KF`X9oLh#t2SPJSoAD1P#bAnR_M-unGd1y|e8scRTHk4o<5HFm7YO1uS*#(#AbR?`FSA-RiJ(TMh+S?VF`Hw%r*ftDNZxRAwI zSw%MeIF1rk=eu?05(B^nrQqorUi;h$5$-;XP=KyM=k*)tX>lahhMUTJKibD_P)jPb zZL|^41{b%G9@5|aGp~H_|M62Ved6?O-|^gU{C&@V>9^g23%I)8=&G5>;n?mL?u+eD z?Ln$S+OFXKn3xWa#xI9HwW~!by96MH@(k^vrS7r5{^|CyI?HVp`<5SO-7k~Ej#w(|1pS?I; z6HHZyy%&k|Bo zAcDB%o@iXM!F^EBQQM{#uAD^}n2cD=zVUAgJinKuKo`N%WhNYg;Y6h7px31y38GL% zM5dxNoom0L(|ZsfNUU&Rlx{N;oq&`tf9Pm*>@SI5blIMhC8*U!$}eYcld?+JD;Bbh z)A79Va#b+PKYJMOz%|dl z$tk_(w94h5IL1OS`^m!v#Ff`KDJyVFA#Go390H6Il~hmpK{^B{5bJ{ULQsCTsl?-D zq(;F(D>J8mCPm`d>aU8G3$gO(WX%WGNyH9zhfSmD13;W4ii48A+;ltF#Km!FbM;5x zW=bpk7T*$4^IRF$NO-S+Re>d-tH&5FGe^){#vImpg;VralPV9ouQQBde13E`;Lt;b zJpVOKZcvsQgOH@)MQLQX{47h&IzY6#3_hi!x`C0{b%b#N)JVrXoqDZ){n#jW8szp8 zS{Rl*b~bu|6`mY>3RzQ;YM;l5WELEQo`!w>bDQo>(Gcz~BoghVS-sSZm>CI6ynnFN z^98SAde5*oG!o8Hj;i_I4z?iT;h`eL%%J^II##=Lo3e9lrE_l^vaVd(nP=09rtiZ$ zgO2+u`B5;p?F$=uS9gBNlWG03p88o%#DT+-|pCIs}%-9pc5P}0I%@x z{kd2E{D1o5E1$joSAOT)e)GTowzq%D1p!?@A}oLxZ6uZgK&O6dKUPr`DAX@j~)ng zGsiI}7ebt@VMmrU@e&kYfFr1k)7fp{81$XAR3_4M8b)+kff}@K-kc(;tIb=Eku-jfJCoLW zNyDo<8kOYFXkicnxxYl-Z zI0(=ma4a8uZf!SAMud<+h)mXU5D>7D9v!<%^3SjUd|e)Uluo{1Rn#3qzsQJV8Uhu> zdeuyw_Tl~Q(S3OK+2idyW!qW}1PVW1rp|~h2bL=Yx8lKk@00U4O^F{>*RvgKvBGg^SZQA-3xVu(XuCGF=GjJ3xprc#+Sj$HNiJfvz9X z)r0MQU;V`I`H$auynN{&{6FC@{-57``rY(}zv1?S`&54%Exf<2L`9w37A|W^U%crG zM*#?dHWsKL)1=*tPeixx;>B%z@Fu~j%~2P>tD4DjH1UC?-Y zf+(7#u2Rx5fnPq4jQxaQ+y35b)&dG6O1D2|>68(1>v5T<2;N|2+)UeGbsSx4(QHK* z!csZN&$6b5?iXVgvT@*>erAT;1<<<*(T+#;3lX0B(dHrC<2NS3gCcpU;;>ADhXdW$ zbRlGew#S>TnnIiD zB~c`o7eYwT^#g5k$SA}G`W|y?d47tu#H3?iy_SmCIO!APvh4!x@k=1th*Az1i#iMb zIPp+_%%nM4@2RFy)t4B7!J$y3AyhNn!~>fSDbsle9B+bSDS2@W$desvo258zB-;|D2%WmveDnGcwvcMdz&`xztWTyaJOxO!Bcl%&OuHu3jX+TDO*QJzw5@Eg%m4z_ov7A`d<&=0^_0N?1k(U* z`=ylqKIw0>e9$+kj-I~rO;W?|xyk&QI7Ku9wAdY(| z{2}w(Q-3`RwSODpF=N`i-hc-37)e%z&FwU`FQ^GLM--KKN62!UF}gdfL!&fA1P>o< zr>pJuT`afYbS2g}9;7HPOp>{?adT9!Eo>`bcTnL8ZA2SgK2iR~k39UVf8sO0__Ghc z={G*}+yBtJo_*os`XL=Jst*Ay{RQdl6H6=dRH4*UM#D?N!v&NLA3fMU@5?X$_5by= z4_v)3y;D zyR>PDaEOg;ad*$rDYFv`cx=yDsVta$KDPbaQBLgM&z+PzWV82tzzdCN{0xxck^rsx zAxVM}^J&&@egcu7C+;gOA7pJf{DD6d7N))i16{}2aY{P75G^SuB(L{qA>ZY<9*0Fc zK0&H}y_z59TZmN7AkqLuE@30xky_b-WJhEsb7SQC_>F|zhongV4%tt6E`#-(&XLr3 zQdLPb{D|T0U{Wd=V+FcOm;!M}i6?W;jxeJfjHT-jq?M*RqQk!B_EEz$y&vn+G965+ zz+XI{)*X2VCHWWji=EA2A(=#_A@E02P!PG*Y-{o&6!_F?;^bX_3zZf8X8h!abK$O5xvX(6m2Y$g>jyixb{JCLwS#U*~Bc@zmM}odK|f-||K` z-ItzHkUf&l&K+B3#UxJulYppeYjfRHd!g#hVtQJp796U*;`5 zoGbm~8EJ+MU+IVzqMQ`->3txVBs+G5zBGwnx4!Ut%B$l54zi%LQiWMr-C~JEcFyWb zQdQbwlE5(VoCD*8_FWiK8+Q2&8Ou6NJW+Zwkz zN7PCLVt<)Mx2O@K`n0u>Q?IrgcW+hz2mE~ET6-GzU;>bNu#K>jz8o57*(+3aqyF@- zX8I>U*bWzULV{AK>Hn2}O%5uWMvkraw6QDzu@#^<0NUVmqT>ZVd=0+uzk2mY|Nd)V z@vENvAOEl3`L+*UJiLFxg4ZW1rIcf5Y)E`SnN~6=0ED8dP5=mn3IH4d5l<)j{I9w7 zn|>d>_)Bm6(BFCZ#s81je$#*O>@~lnjFgCh08c>Dj}J@)*qVYxX5fnv)}!*Hr@At?h45$uAGtj+fO+( zi=G5v<=8`iLpH&B1q%^eQ+3JOO>8hqRU{hbHvy|7g|M^bJImV0_unZ$q*fvN4<*J8 z0Qzq*ibEm7cHCd^IEbSetAOjg38*ZeP{Z6d*eOB)gG}a=Fupb;H1-aF(I|^3L$flo_?Js+!3yr{i3BHu-tN8X zi9i!l%4!PLO@EdaBW)p`ROBpec$DwN>vF}BvAOjWZz2nyMAqybHCw;b4KMJ%OpAZ? zL#ZT7a#4l%!FYyf$voXA7a~syl?Z|nSRAD!c}|T_jPj>KDp{ZI%t@OWlo(7iHqcf{CEkV@dg?I(2v6C zhexEFaJCayl9RhTZ#rW10{E4P*7D`P9T3GBQ_SPGOI$*M8bx~EI9XptmLk1W9VWvU zqkGEJ;|*VI5^*n&S<)2D+hoZ2EdEWn@Bnd~9O{hX6Z`Gw|Nr-HW2!#_Luk;r%)>GQa}t}<;2qH=@|iT?Pi6 z!|4jP(?%P>hCl%IgA#maofzS(_@GxyZTA5HsqtR`3$&*+Z!_!nT8^<>-gO-lV`-sQHHit^a(XGcXLxg9WbYh0uSBcQE zSN;&1(qj;$sf$~tfk1-*g}df>iDV*=>?~hHJvAh%?@uOe$D_K$((K7`&x3`|XF-(t zsqh9um@B=PlKTMNX#^12bJMNJR(v95wrHuV;+~}#J00-Se3&+CDI*ZK#ezWB!R4-w zD?95dZb_d=GG$-2;g?LpLh6W_Mf{kDW2`tmn^Jg@C9jzIXHlKmB|?6$^}-u~XU2Ge ztbRSJrT|_LRB)01T`^ecNnCzmrn3Y4$HnbGHy(xk&v1HdBF=W@)=aF}j7pce0uOX` zhWA?X2|j8^p&^(oM-*v@dCOZ`0pSj^vtDYL?>S-$@eq0EGdklhGjkq#55^Fsk zIAZOi6_YE7M?8i=_t%TbqUPWxxD$#rC<>sM`)wiAry@`F(Scu6;EXa!#>~sF`48is ze=^T^2>bKw<5VI|?`Y8ds;OFk76K*@b`5z5!4vV>zH=F^jGvZ>j#ZHO+dgk&r-?S& zPCx|Zh!+=(64ISI(RO{^9t=Yus<+UMo*H%>XKV3hC|9>=`(T#RW|k#S8`$*X`lQxAUYzxl4O z`px$?A}r;eF7&|c8qes?N>R39DGo*jaMh1RytDs}Z6`-Zp` z)as?SB2ZPW!2abIvWr*tooHuHXHKAbZKFf`d2HV#Ib&jU67Od8&kWB-$tBuAOrEXi z%I<8-)Jq(i%44(|vwX%2xom@;U}03?z*)BXXRF{rcWg!cVc1mvQuP`=Qs@rL14fM- zaeS~E=r+Qrv@$mHE1U@ph^m8EJd^Z#HUwJWPEEF@sHt_I*5oUh%3xUU}>oF?GG$A`!j@w6bK6*e2K(dK>BN=fW25W(Z zH=YB1Q~x?-#38wsKp-pIY7}6__34Wu0mfx4@?u62|NH8kEH)tM0bAcnN$S~NU_Fgt z%q$bd9vM1wi;f{%WWo)%h>03~xlAcfDPd;D5Co-NmJ{k2b#9FOIJqp+)w89P(ERFR zrP!ylRf52ZP5tpMsITM--Jl6W{M?m@tMO657d}$wm0(OVHfu(U%+fmX6d`B{8s2O? zW!XmRWW4>n#Zm(CxG)pVy)u}`=RJNKa}9PP&m;czfy#A+If(+DRP8RVOn#@ObSpo;ax1F8)neJ1sXYK{_6(cywVh zi)Yx+N|Uqw1v4#)-bcE)Bj)Q{yXlHqDmOY z&#CUd_daQ!$%z>ThB)K^NkF#0oP%TmU0wAxE4t#lz6KP-8rB@w)y1%ebzRJ2KoA8~ zP~sqxbI#1bB$)8bJn7!sUFY{lbyb}T-S-ae_w)Ic@=V|Es#E8jI(1T4S5;N3Y5u}= zN_6u3kgK_7^#yZ|wikg&*MW}rQ1y@iSEAgfc?LnWIGdS!Bh%Kg_3UO*&K5}*XN`Dv2Cqc>08 z_Q%N`7qkvOW3V+Bi$W%e-I&`>EoWR2>{46(ocvTKm=B?eh)@<(6msd_$-YOWzxnC( z!#5@;zA+zJlub<*vI|9*1juqzGKI9O#Yt;gz2~#3r}`-)9xK)W>c}gNpmuSH+}Ob3 zvi>hhHoxH+CLd18Cf6!cpkXlpQK=1hAQz$@iAJi;cHgd3ym>FL8hs{KM4okyMftB2 zA(Z!=-{jtMht9$e>{>ivs5fabd=$^nO%fDdN$GmzZ#$091-etzW0QaeVu_Qj?PF*(w@Pcow*N67!wo zYrOi1T$;$#P5JeUJOoqBduY0@p8b_)nIU_38zr8{QwDymn{TlKP>?N*{wyT3G1U!Y zX-)2rh0c^$$K0tKkrxobl@Ln%O|_8~ zTd9J^dS}&P8vju`H-2l<-^RL@$?JiW#^Ve~btn}j-a*EjVAU+^L+!J6vD_pWXQ&!} zrN*y$r(wLJ8qp6`NmVL6K!A^pWnHaoA%|CGnb=?ZG|b?>;Ixb1Uu>54)Gz%^GgF~T zO;d2ri+yUEgXYxVmz}lHf=5R0lBbL#MLX_lgBt@=swN~QPyuN}q?s7sR6KqE*fS4K zuYPcP!|L|f#_rTq-f8D~K>(DbNt%deON@-B^LA)0+HGK`eOkNkKVxK30sx)q0tE_z zfP}(WXw)c}o&Bx3(pdp^>VXl`a<>FFYwTsE2c&!eAyDlKR6YULpxuGs6?WZv%^Pd`@NcKO(D#|*ccIJ&d}DabZaSE8`YQ=f0? zlh4a9sLD)HtS4L4n>aTas2KzVvdo`F*FQ3$bRkElV2#}g^jid%Saj<~OU7bXyU-QT z2|&HNWMHH0(we#239Ap6qh4N2-6?iyueVeMDtKW2T&yGV{?%92Dhi`jThR zvRBRO@u(BkI}~?%tcX*b-rNi0dX@HxO+#hn*lf2V;4RYWJ)dIZ;`lqzpSZFh+0hVC z8dU^43jq8V9^7RWt&A$YI?&no?=>UvgyPpLZzL8tU zKOi;$sQu$w(gUX-Yjzql0uj1#M#r%aO2dOxBHEeVS30rx462dKN)5C<(416m9}^%7 z6Jowvm?5?Zn_gpVuj&r5ashu7E1OAnANb&%9dUh3%~v;40uW@aWPDTpz+boAechHP z?wZ`Pp<4i?O-!329l&fj6Y6^%g1n&VE^J%he)_2?x_;yAx!Laf&e-qR(fy8}IkGtE zc4U!Dksy|_gpmtnnt%w(&{O-unv)3SL$!f28jN*7=6>j)+IjvH+Ha_cODd|&ifL#P zN4kzg%@Q^}U9YRGEC7f=DqJCJVv=H#A^>b%N2~7d4$i>cjv1J-G%X4#3qS-+EQP?- zX3ZRNJRUM*Z8xqK1VsVZf<=3$qf3*=9_#$#ybY}%uV1uaXvLvJdmYu<`M@Cd*+D+v;ZV(B3UEb`=(z z-erZbk64=)Ifh{>zkr--Qwt_4%RWX!0I>BEm-y)CtXymn7l-adi)1 zm)3&IQV4!AFrX7iRp@aL)(&mIPgdDV=OJ97(!Ir^{SZW1Q$w>KJAg|Xi$lzW2<C8uY^_+xj)84n;3nSgGDy@H9{5gz;Es2q^DAi0~2;_Dl=AkZg)m7LeD|L^tuQEqU$@OHlF&0Tx1_LtIYbJrjZnF zA8if7vHY9WW>q7A4qcW4SJag^5aFQ(1t=!Z&O)TEdAewn(eum25_y64{ z4yvYWB2PYzPtwRX53SwRGl`AU&ZVHq7I%R;vbr(C3 z-eTn@jC`E?i{7nO^zJYz3Lk+dmYg{rQQj1bPU@P0aGeV6cl(dF!*W&mE!zbogjRE> zrt}g&!)-pD`|@617&#$UJqQeEFO^Y=ANjgsv|Ytlodf`c#LrWyC|c$MgB#Vx2)#bS za@4UWNr6*e{@=Ix>|XyR$Rjw3ApV5UQvS$f?kTnrjQLX38fQdQl}(B$?m$%OZ;l4n z0)AUIgyvDDge#)l0HZWK)#0~Gp7xWgJbS!r^iHGKtQrfX@)?_@*Ag1s4Q40;VvmDkA^_{bo)dysiB`RmY@s)ilugBp`EW zfIbx7ZWyGJI3!6*0HmxX%DrJaFt$FQ9FtVQV>fl4exy5ZX={fS83-ua001OOtd&rj#m^!V-5w@r2bc+u9q_H7<{>d0}g zowekUR?(F*2TTxTuz`_fCD|MLMIq_*m~nZ5ua`0mmk1!$#`eJuBjxKOUk$qsAE!+# zlei|WD%O^`F?75bgW*~nM>jjB!Tw`qHG&y())4VHip@av3!V2O5e;RL_4iXSM7SoN zyvnAz2hiV3!+B({&>3bVAc4-7Y6PG@y|)I73lxupv;tRnx=Kmoj3~@o1;Z=;?lMGI zN2?rvZf443>e!(wcE=5yVyO@qhrfEvjY2^0y}#a|3Rv!^u2c$jYm+mc}ZH&*&#f0Y9rPC>J= zNI!X;5a-ZquYlutSynb9St)D4K_BnN5%Jtm4OAfjdi7h#f!sJE{<51JJ#V5#=K|{GoQ{!=Nq+P`Lm=|V&B8b`5M8&WE}~nrvA*obYeZm< zcB(>^tETOAAH2Z6x_hfYSb8`MgDD|s86WE^x#$5~GRCvhz!gmQBG?b6V%}D^uxUCNFJblN% z$@Fa@u(5P|!Dt=oHQ^i*n4)(x-}Mmu3Nddo;+eUXt-sD%^@d{~xdT&`y!U$de)f}3 zsee>yz1->Ku&mbItjSgT>X{|U{M-%C0eBZWurxjl(@p+vHbhi(A!~?k2XDE2!<9c= z{mjFifx*_C1!;j)KtWvsREZcw69N+u5lJWrBuN69HjbN;FQS-KL?LPZ(=jr>pPd+sL{Bs?ers>W2>>7E*DXoReTb52s zx^M5j5)FzFk$E*hNXXX35W(yP*(=|(;ONswhX=FCZF2P^-TQBxyyxc0AD_4B{u`%W zea_tdUpb@5NOl1A!gj@IO)F}%@8hEtoQoLgKN3)q%IB~%p+gWlZ5E-|pAOs+43l1= z@6Ve#>Xm6{J(sI&(BJAKm++3l=>z=($H8a_QtbXv$1a_+-Jm`Q1e99*Gj|gz{dsz0nCvI z=s#7r^+kYUw|&I+wb0q{q@2JoO6IpHC1jLX2)LVtM~GY#t^OtoSg|?q$Q& z9aDzfBjQK~2B#m)%WwgliAexXCbUYV!!UtarASx0$#x8o8(PQfY?mZQE7#95uVIzz z&hN14W^VRIqi_pobdx9$W--h)Hh^*cw*!G>*}}cr`S51Dh57eB8s~fZ1ye zg4eIMLFv_1xbn==vW{1!a>DXYT05~Ez3CYeVZ_nHgN)N-oO_i?QmOH(2eDS~=QF8D|x@CAlt7bUaFj1w|y*(o%N zNqTw+AfkkJK5%g70|yoK!vRiuq;uC*6IWlj^{yMnpZn;Vw@ns@ziAdFB#ZJ>DMUcj zpR1Xjn02nL9Ao1+BJ)!v|E{mZUh7Q97uF5X5R7aV^S5(S`rp!f69l#P483S zrK8Q#7erEZZj_$Q{Giv8qXqtNbZVL<8|I$Pgy>DFbsT|uBn_YIO4KKyiX=Y`eO(2s)ZG+Ka0B>nC zch0uxJ&F})$H?1f!uKa$Xz^YNvRA3ZnBmM>(3I*(kJ!%$I^Nrz@Vy;>e;Wo-3VQhF zxhGMauKJ=hK)8O#!IERe+v>l)W|iL~#3q{OCb0|O$6yy2^-@%+IsgkC9-5KmJpz%( zz%chj+#fbT80U0O+iw`Z(f4u=47bgHH8otbk3QXbQ9rL=En@Mq43r#w9t`6N^Uta((m+$oyft6`?uXG)*-$0Gnw^Ejm@EZUw@ z+EO!>DzokVz=~(7`UO~X*wzc4zx@|C7mPJ%^kg!h=3d+SJI+%8aJ4N&uhG7O@;wIY z4#oG>@rxQPrb&DZnQLL(3=_(DMmhBxGK7005+b}E{#-R&jcm*+bJo?N$a6RB0zA)v zwBj)gtHK5XQDC|$Iw3X7dzDbf#a_ZVpeXjjp=u^j-x?>HuL<=oK71z`?Gt`@OVeA41Gu_g3f(nA$ z)&{7WJQn+y&mPL8mABikD_^?34(;q<5!z**X0EF?j1<)sx_7lvY+qJJlY7%p? z!p4z7G|j`_4WU(@WdD(2HGg{G6XboitMKWdI^tb;b$rvrKWbx!fHcZgI5XYnVvc&z zkSkOKj(G5nh=XN_gE884P9p1S1HrVVP(%asiKcaGr#<49_ZZ*q85vnJ{6ER=#Kq8_95xd=da3rM! z{&2yn)&RZqeLJLsn0HA4B7jJ2B9SXM9u4?m0fIspP_pGZ$_*l&I52qx0I)|Nkj@IR zH50xUOZE|zDJ%ezq*FbTh-3k+!DMP3t^CpWZ~tfG{ZDRPJTrUQ8)hDK(u}>17@o5t zi4307k-~hV@5f zyJJI0yzFP&F8S|GOZRUKEl*_|s61Ct?MjW_zSW?*eH}Ab^*~!(Yhp{@=|Bs47D1GM^kA?siAy5yP?$AUoEg9+P_xHa7zDc; zHz~GjPu>V;M9ng$RqZ|yci=c$@xpY78X}W$bsAP!dVjEWWn<<_iV^fn6^kbZzPaE+ zT>`K}s@J=k%3AuZfpTktUiRijT1U;F z{RJnUq44Cy+a;tg)VQx$VVtWVY`1S+fq(FRkD^ zt{1iK`XwKP85?cGOUjL!OAXUj+qGV&U&>QyVRC)1h=bUsd*}Bgowa|X`?vd zFF4)cm3}f8rqr;`3vpGVwthYW+sZ1)Ax0XJHtSV6;y!h$wJk!m0wb|ucfsCgv5>R2 z$9~vbKMK{5BavD^mHEMQVQ*e#H!%U${@RYpsnIht7{k=5i3pq%HQa{S+^MSE3m;?J zox+4FJ~VtIHXhekxNYPbm%b()Pl+;54*=1ur$PX`vP;uh*0CgR$Br%vQvx7gzHBb2 z{FjksPa7r=QdxLf2TjU}FH8248|J5x6yFOV36Tf99{`FBe>mU5jHWw6*0P3r%?wgy zp3kL%J z!=-5bXfNRM))~H9N4Os6no2xdq?}sb#_<^uOz5cZ5U)m>h$KsHzGU;I=RbvcGCHFr z^DYPk6zT&j0Z0G>FaePWfrTt`DGMSX6p)EjA{LU=$4@|jBme*qD8n`tgQJ;*{13l) zcEQdA$G&c^Oe7JA#3*|vUFH0zGE3yi4-3kG1>@#l6IzjSc}=E9YhKi6xR*+)Y;NEh z+15Rli;&3tloDD)$+l1+U?LdqfskRXc^nKZ>D z0|4VdYPwwMGm6Zl9>IRg;jR3#@@4cKlYw$G^#n*t)E^i@w?i{`%wGB7*-t;xe&E)L z+b`U5;z#EfEut=vB;+4qN$zf!2RIqdTa`m4%SclBeHw8xoY7$flf&qW8CR9CASm+` zzHbWv{6j6>X+#Za4(c=~f~!_d`_OSQRx485P^I%?Y=H@cft&K@b-l=HGSqtAN~6Y{ zQ=#w+=<`jL@zAjRV%OkeT^5R*l;Cp&nuzRaABbRGD0mCW(#^HLKg8L%u7qh}?X z_ufU%5{+Orx}{^lB#<*F-j~Z{^eP8U0-bEYBL-}!FpqussBG>8Hp}hRwCy>ktSPK@ zJ`J3z;JhX#s_HR1AFG_|YG52aAK|(-aW^BpFcZP}U6#^n#CmjXfOB^B6QuN0LQU(9 zQ7jPiJC98b)7(>H0Kg^F2jW)PE<)`1YSD{GewD(hPpeg#A*hrP?qF6;c|Z9OmgodA zIT_cE*Nr~0*OPHn0C;#G^&6Zny+<`fNF7zV{n@<;U3p^DFNdF3zGEnP9R(;)*gr!wWDkTg+mz$1Q0 ztA3>4vvD6)b1K4KCAiYM{sagpj5-Wy@*+Z$q$~hxYF!tyek)cUQ zEHW+iqDTmZT2Tc6)NR9@JsU56*YG#rz3tY&boV@_z4(wO3;+OjrzJ?D1b_&n5C)XB z?S2@pWY(n2)FYE`k~-ul({w?~465h2YpBfK@(&M3ytNy4-uVFZWsJ1ipO3jXz3fO2Hs@&m!u( zhE@zf+h03#OEQ`3=LL0T4iilVNIQ%O6)~G{u65d`tQ%|+V}`#RI&SbOAT*e3x?zcz zVE0`=*|;MvmsmEF+y?qW8uX)PsLH;pqw0vwMBXU4)6;3Ono`P$rb3_Z8vEG0`kuZi zucPZ8T&|O z-}7=Sn->iR`LIx_XeuThU)~ks9*|Qa)=&1+PL0~dM?HsfyZqsrgsJI^-{hN1ddT$3 z4jY!*PZ!kls#Y~! z*9ddzZrCM1AOryjmJTyYptp5E_PP_DLm}$hRWiSywsBG5X zWUET(M3taXI^{(iAn|6V8pY~(iCcX^x~WMMMrMntX`ZsKEB(bVzYyvaDkD9FUP;`D zU&*?t(XC;Yq-H*EZ<;)cGX;YfMYtwO9EDE?Ao#54PXf`+nb?E~5s3sl6L8h9o?Z3e zCHE1DbmsGdOpbtdkeAOCnKxv=SrKe$ypTEQhV+v<-Nf zMN$9xi`%sf^Fxck3vrF7>`_Khih1k>0O2$@|B{_NW>JmjB{Qg2CukeTJF;#cr%rht z`o1&THm)}c*EU95rL>nk28cN&0Cv}&pi*4Zq9xp+)oOSCft14wi0#L3#x8`!E1B_P zX4H;W#4%vD%=8{rkT#;CpjG%*D6S>RAZhrn61*xGsmI<=5fVutP#c$`J$u+d(O`6% zm0UqFJgG2YYBu=pU}&dYnjF0s>;MwYBRE(`I1`uTN^{fmU1fBj)_tfz zY8tu*+U81FpyGXlS?_pQP$B-N7E~0QbM)1b=QVlv9pG+Krg9!c;}$68*;%H>m}V7b z6_DuIv1A-+w4K(`ZPUDg=t%;FFRqysaHrZ8Ct^KDD_dkdM090?SXjp=fp;AS1s>bI z3!eH3-vWl1d(C5r`6{Kp8pAVQKb*x?czQp3I zZ0*eQYr0|(o$|i^kCt!*`x$Nza!d@k`iuiKbCxS4bKy!t`EL}A_L`#liu4e0I8L=7 z4z?|0VIX)K3<9yK5+P<24ZAo#%%)#=pI2^K_1cZkNJ5+1QTn=EVK)`(w0NOe6M-EkwX-m{66z^2FV~= z-@b_$DwO){lIMf?I;dK9?cu>xCJO+OB#?x(DIU0G>mAoUpF+}VWfCM}3M2>wq96d2 zA`_xL-5wnkN4#Y2p)a1faGw;IgiJj@`|g#)*Il$>?IT5&rUDB+Y$OCqi3tixNC^%M z6?fgR`G6xw_dRu(P*M&6fC5pJ5gMw49+W@jsSPWuYVxD^!0@1I2vIloEs@#ow|pQ~ z_J=nmB1}LO;`*O&yXdDI2S?yF@0xSm{~hXzq8P)pg-L>lpkqQiTa06ffs^VxLGFD5 ztKjVHsUBngl?)kbb<~RjM2aFI1em|1dBST(JNcF^>+GQ-` z-QEo%GuRt5HX!6ZEhQ6!w>NzBfYl)0(D@L|!%fX~GPMb$_j&>R2F`3D@1~naUJ_U{Ttz|oo@HpI|-Q5<@BVL|tk zgec??gr``d$kY@*9m?&)9O*VA=(Gwp+6#Rf&o%Q53OI%+V*M*g;Fb0EAKWznA`uDz zM9x|wMqS+fV2|Q4k0NVrkFTnHtiWA#Pzqydw?sy{T8Oqox~kmE zSh!n-8+i9U#F%C9YkK{Llu)#mij<&`G&q<&|J?MQ z*KXePu)%Z|c1R+k{)|WA9cnvwpIOp`My8@N#bp)&brVmZWeJ4Xn}AX}C6F%^1c)d> zQh@`*;-Ra$zyHyeEnBAE^3M53zjcOeNjZ*527u){ozPtIfz+5NxP}sD7)?Ia|In5o zHe18$LZ^?8h_%h&Ko~G=a{$5 zXv`Fy=^|+W*!+;90QBMUV0ly#wdn1a?R|5iHJ{jh=qRR-!fB$nwDy=|KuPrti4bMV z&CQh=Urql*45|+>K+G%!53waPj=sq;ciD1hs*)LpPvli7b8U!#M3RtgBI}djayu;S zz%NhB>t$cy3;=>Y?1_g2oEOAKmNSM;TrCa^4Q7BnLqg6w|2kUYk39S2)S*)8_@rxN zPgF>Wh)I$FfRs{}KLsNj?b169-LXDgT?eljLWeALY1kt)Z1n6KOCoTq?M$nRObu~s z7=Daj<1Y3zm(e>l&F!O@mtZH9^5WfzX1c5DvEFvGR^ib+HBO7+Y0Swar{8|8znQQ4 zCwM~^duPc<3Rx4*8;*Y{c%?8|*J<4U+%NFq|Rse`4ubG`f=I})Jj_E%Psj3s+kCtfA9<_$iAheIPrUMwj*kaUX+-d0e zn9d_-S1(gp_(A{$gsV}v%9Z?s?tuo$4I3h7naaSa)$#?l;K&%UY%Rr%jPF({peY&N+f7R%yO3+1OQo-Fhwe!d2sTlmG94{x{?I-oNemqlZpCd)B}_(e22_V4dR| z!4Nm-IP$Z_DHoLUs6im6VrJt%f+n@-C?k6^mjp0i_G?KK0EI=dxSmw$M+Dh-UscU>$_Q9(DRIG|A=6*lkhNmXH_uslt zfBwosn%!v=#84{&SC($a0tC4$MWU`!1$mZoOQzS5Pf82i*N3FP3Na2Mk5>@@NvIv4 zBoRoN2w}9MwLwuFL!@1;qH!eD84n$Cpu$Vla9HHCpuBXyxC4j}MTxVogsUxPPlf4H z=ctV^9CFkoSzm^Tgb*UJXWOM|d0c~(1sIhBTJ2PqDA6@rOBm9_eH_fClzcUiLC3aL zs=O)?GVs{n5-<0TW4k?6YupJB^FGJRQsmud=rRlCJnGp?ln+b`*?7-0`k^FhXU|}z zkC3x{@qUB3#jtgw2yjm$1&FM&u>ibt;S5x!RUjsrX5Y8EW0>61L&8Cm!bs>xkv#kC)PuK-FWY}G$xs$% zk&B;^By-G@(J@x<$60FW<@_k`s5&OmQyOc~bH_4~pGb^Ak^r#L5O@Er{nsld=Z)ei zZ=SXAfPo^HX;v-{5`AbqCJ%PXm*b@;LK#A^_#o5?XioMbn>E_tz$|_XO z$8tG?yH=N!hh()`YzlZx(@f#+sv_7QXDbp33QR~!MB{pC&b#*0;9_lxIYW&}8dDS{ zFM#21I_Jy}LT+=rTx!DyDzqaieNwJ0Gj2O#A_M|RNs5gKBFzNqvA6t*UNa;jod)KB z%Ef!Y&soxP7t#q;>t}(_Q1Y)Tk zJD?D2*R6f_nP-+PS+Z!+Vku?0e$7!kk&b!HfTaXfB{xeIr-asE=NTi=avQba=Ca4M z^6z_s5?5x`Fg*rDsf$Akn?yl-E}yg1q=Lp6OszAmD_9C`NODi3Py7vwR%&FHCfZd$ zwgtc7oigXd;>p~@s^Zv0j;E`Cz-3|S=0SY5LPYRF4)EB?DkvARP;Mgx1T(kB^@=tO zQFSXg)4?ktyI6Bm$A4l($djue#I5xAT%3L7A9|A2%!v<{OUQPo396i1o)-vviGo$i z*~@@b1om-gA?-DViy~^Tx5^u@BCtXgSPjV{Esj{cZM0L)lPD(jB`QwdqbTJ1k*&a_ zQa)qecnE|;uJZTDyWlg)N5|!G5Y|PsJ-d!fv8u@xGMN(|dO*xH@dy_X{V`>dYcMD%8oMWdz{s|`t`Ou=*3sZX!tzBsW|z!o!A>QVCQ z^CER2UHSkLxz|tcs_0P0uXLU5e3aydAf&RIAhC3V|fl7%dCbq;!wL!J}mP_&B#pgm2G-?eqy+D@9F+E`R5 zAptOl=Y%iy)z-~gK1<(OpgP{i*p~ADSRNh4K8xas7i$~P{)+%e>i>um0f>;Kph9F# zY;UGpubzJTsrKQ=jO=&(a3QJag3L8U;fPWsS`NHDh)rZWl4f$1qlH97QXR-eNT@?- z(r-3OGl{5Iu%JJSmHZNc+WAH#m}Vj&oZM2(+BJFE$7i1Te}-x6^zXj6?y>!OQ66rOirwmPcxLYrfP~iJTpYt|jh?mPD!Ph!0AgDY)|Pja3$TB<)f5F zUP?6D?asvHlq5odBoGC*r#n-VlLdQ4NnrG1#AwB>r>muj0U*+=y~-)-RCN^)T3AYM zBV2M7RUhhRTO*b~v6vg>(phRbPjn$S$=v5Iye@Cf_3cQmjsvgd~f96#=QAD*?_T73b z;l@EXT?;b13ko3wI&0`y7tSnTPNKO_QbIU0xG)+CWV8?UQS)rh`g|}ozN;ZQa^AwlU#Pi zWuDiGvdd$8^g0>@MWIDWFlMgmQFtGie1`XRU$#@N4?X?EWvp^uyV_3~L7Q;kbpXs1 zh_EY41fS&|Y5clI{wq`#^9glD<8QFG)u=HHgT5S2_ME>}r8GHFLjn%pjX_ZUV~@m5 zT++Rc`!5Ipsd6sOaN7vO89X}>arzPLvWBu%JualYS|@mUVY-Q7#xdpqQ7dE9fptLG zWa4maWu^Uj+kR|hq>c)n$}BGtN3X2zF#s7ujUS_d7?^eVIBLLx?a_+4r;$17-PcHx zcFW@LtUMr|j$^9L-Z6buA&eM(wdMTuG^wi1N7mBdaF~Cn)?(sbi zA0Y`M0VL7wQj!%YK}tX%DM_C>ML;A{0g^Uvn3~wy9fhVFZDEN*y-Km-DGmSyk^~}1QhfBD&K);Q4h|H19o?F>tl7?i2v9-4Cl)G{56})t^~ zB3TY-OD$zZakO;zpyP57bnMAbO`e*&%Z5D@PHHi%}vPT@r;F z8mY`%)u#Sd`~q)}U3fPjNs@Kz)?IkvZ`Q6|f99E|AA9VvMIoPh`pF;v@M$yn0ysjsW+|h^w?1!cPQ#PrHZJFya9bE2v$a8HF zCa~&Js?eD}4@2`8-^1&|(?(V{Ld#xLIOg7UREv6)RxviLALVDn z?z{JEcx* z#o-ss&h!F8bt2vVz1*uPwfg;dz_ zbJt9lYmWLluM43^#My80eBHP>)H*?)RgW9+USNEv!5!Byh^ksu0HddP)MzEBo+2Ko zaK{0!aLCnLzAM+Qjs#x9tq?+WVET&EUjLJ09=V1xL&~i>Uo>Gd0UUmiBT>s9$~|o- zw|1wt<;_f_DatY@L_m_Hdhd(?kdtgR(_NR$+d9?x<1e2cNSoNisfmr#lh~a~S|c=b zP6Gibfypsi_w@912bv>LkUFeK0w72tp*{yE0BmG1JylF?(|Z%B@Ym=vf5#j11MR;k z+${lI-`7nIS+3ZxJ^;#3%`9Wp4*@^|wB-gOLIEK0$p@yNeqwru#p%xbG^vF&DYf4s zm?!~|b$R{Fjz6dfc5mItp0$2kA^=i5T|>z*Z|(?USU9XxPN;gFXil<5tzr={!}d7M zUXdMl_RPQDF?s*W?y8lY<)=6DuJ+yR?joS$at~20ou-ocLQ3^#M+e-EsUxMz)+zwZ zrMC6+B9~I8X-Y&_Uiqhge#id>fP)V{WVc;++iS1AKoX{QH!kJWkV;pSpEHu#pdjdw zVO8Q(?kesg%*+uX`(BNO6V?eN(1{ebNXH1i$%;4}NRnjTy7lLs_qEllpB^3_KJK_< z0pQ_BANk@JKbMMZryZ9aa_AvyHH#4dyHY@P%e4WqzwD9z$j_;T`fGe2Cznka-0}e# zRizOM6B9})M?2Ces(+2&$GRwol1-~>9iJqL+6b8diaa+?9uXx;^4xRJefl$>UiI{< z6Hh$x8{hoKym|AAJU{i6Q$G3WkH7Q(z4LqD`OYaPo^<3t@=hF#}h^;ZCHBAvuy?VUc+(!7HHqhJ$BP z!a%}SbLmtUew{dP!5U20w+XQgiwS2v)y1@~#)>&H2Aa+vnl3E40Z%Lz9wB-s9fES5 zzoYNjKI~4|Rj+ITdWc6XkaI{6IO#ZAUJ9K*aIdZ4RrOod-28MyaSUjW(

s4gGDT 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

RjXZaT_wT=gc!+D~@Sq|?_3=;Rhs|4|CxC>4{p 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%0DdL`Mqkz8hrH6t=MqRL^Y(q-?>38JA45F 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 W)IBQs5x{Q+F9<%*_JcL>pn8-^~4F-ll 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®edswz|U7z_q0R;>8Y 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

O@{SIY2no?( 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 N002ovPDHLkV1gv%LxlhU literal 0 HcmV?d00001 diff --git a/sdk/android/connection-behaviour.mdx b/sdk/android/connection-behaviour.mdx index 259c66f7e..4aa6c16ac 100644 --- a/sdk/android/connection-behaviour.mdx +++ b/sdk/android/connection-behaviour.mdx @@ -40,7 +40,7 @@ On login, the SDK logs the user in, saves their details locally, and creates a W The default mode. The SDK automatically establishes and maintains the WebSocket connection. Set `autoEstablishSocketConnection(true)` (or omit it — auto mode is the default). - + CometChat automatic WebSocket handling flow showing login, automatic connection, background disconnect, and auto reconnect on return to foreground | App State | Behaviour | @@ -59,7 +59,7 @@ Set `autoEstablishSocketConnection(false)` to take control of the WebSocket conn By default in manual mode, the SDK disconnects after 30 seconds in the background if no pings are received. Call `CometChat.ping()` within 30 seconds to keep the connection alive. - + CometChat manual WebSocket handling flow showing manual connect, 30-second background timeout, ping keep alive, optional disconnect, and reconnect flow | App State | Behaviour | diff --git a/sdk/android/v5/connection-behaviour.mdx b/sdk/android/v5/connection-behaviour.mdx index 02a960181..36afd6ee8 100644 --- a/sdk/android/v5/connection-behaviour.mdx +++ b/sdk/android/v5/connection-behaviour.mdx @@ -25,7 +25,7 @@ CometChat SDK default connection behaviour is auto mode. Auto mode, the SDK auto To enable auto mode, you need to set the autoEstablishSocketConnection() method of AppSettings builder class to true. If you do not set this, the SDK will automatically apply the auto mode as the default behaviour for the WebSocket connection. - + CometChat automatic WebSocket handling flow showing login, automatic connection, background disconnect, and auto reconnect on return to foreground | App State | Behaviour | @@ -48,7 +48,7 @@ To keep the WebSocket connection alive even if your app goes in the background, If you do not call the `CometChat.ping()` method within 30 seconds, the SDK will disconnect the WebSocket connection. This means that you will lose any messages that are sent to your app while it is in the background. - + CometChat manual WebSocket handling flow showing manual connect, 30-second background timeout, ping keep alive, optional disconnect, and reconnect flow | App State | Behaviour | diff --git a/sdk/flutter/connection-behaviour.mdx b/sdk/flutter/connection-behaviour.mdx index fb2e027ee..568b22c4d 100644 --- a/sdk/flutter/connection-behaviour.mdx +++ b/sdk/flutter/connection-behaviour.mdx @@ -55,7 +55,7 @@ CometChat SDK default connection behaviour is auto mode. Auto mode, the SDK auto To enable auto mode, you need to set the `autoEstablishSocketConnection()` method of AppSettings builder class to true. If you do not set this, the SDK will automatically apply the auto mode as the default behaviour for the WebSocket connection. - + CometChat automatic WebSocket handling flow showing login, automatic connection, background disconnect, and auto reconnect on return to foreground | App State | Behaviour | @@ -81,7 +81,7 @@ To keep the WebSocket connection alive even if your app goes in the background, If you do not call the `CometChat.ping()` method within 30 seconds, the SDK will disconnect the WebSocket connection. This means that you will lose any messages that are sent to your app while it is in the background. - + CometChat manual WebSocket handling flow showing manual connect, 30-second background timeout, ping keep alive, optional disconnect, and reconnect flow | App State | Behaviour | diff --git a/sdk/flutter/v5/connection-behaviour.mdx b/sdk/flutter/v5/connection-behaviour.mdx index ba5b683a9..76557985f 100644 --- a/sdk/flutter/v5/connection-behaviour.mdx +++ b/sdk/flutter/v5/connection-behaviour.mdx @@ -25,7 +25,7 @@ CometChat SDK default connection behaviour is auto mode. Auto mode, the SDK auto To enable auto mode, you need to set the `autoEstablishSocketConnection()` method of AppSettings builder class to true. If you do not set this, the SDK will automatically apply the auto mode as the default behaviour for the WebSocket connection. - + CometChat automatic WebSocket handling flow showing login, automatic connection, background disconnect, and auto reconnect on return to foreground | App State | Behaviour | @@ -51,7 +51,7 @@ To keep the WebSocket connection alive even if your app goes in the background, If you do not call the `CometChat.ping()` method within 30 seconds, the SDK will disconnect the WebSocket connection. This means that you will lose any messages that are sent to your app while it is in the background. - + CometChat manual WebSocket handling flow showing manual connect, 30-second background timeout, ping keep alive, optional disconnect, and reconnect flow | App State | Behaviour | diff --git a/sdk/ios/web-socket-connection-behaviour.mdx b/sdk/ios/web-socket-connection-behaviour.mdx index f2c96d97b..5ef7ddb55 100644 --- a/sdk/ios/web-socket-connection-behaviour.mdx +++ b/sdk/ios/web-socket-connection-behaviour.mdx @@ -36,7 +36,7 @@ CometChat SDK default connection behaviour is auto mode. The SDK automatically e To enable auto mode, set the `autoEstablishSocketConnection()` method of AppSettings builder class to `true`. If you do not set this, the SDK will automatically apply auto mode as the default behaviour. - + CometChat automatic WebSocket handling flow showing login, automatic connection, background disconnect, and auto reconnect on return to foreground | App State | Behaviour | @@ -61,7 +61,7 @@ To keep the WebSocket connection alive in the background, call the `CometChat.pi If you do not call `CometChat.ping()` within 30 seconds, the SDK will disconnect the WebSocket connection. - + CometChat manual WebSocket handling flow showing manual connect, 30-second background timeout, ping keep alive, optional disconnect, and reconnect flow | App State | Behaviour | diff --git a/sdk/react-native/managing-web-sockets-connections-manually.mdx b/sdk/react-native/managing-web-sockets-connections-manually.mdx index ec7e84ce5..299a276a4 100644 --- a/sdk/react-native/managing-web-sockets-connections-manually.mdx +++ b/sdk/react-native/managing-web-sockets-connections-manually.mdx @@ -33,7 +33,7 @@ When `autoEstablishSocketConnection` is `true` (the default): 3. The user immediately starts receiving real-time messages, presence updates, and call events - + CometChat automatic WebSocket handling flow showing login, automatic connection, background disconnect, and auto reconnect on return to foreground | App State | Behavior | @@ -96,6 +96,10 @@ CometChat.init(appID, appSetting).then( In manual mode, the SDK will disconnect the WebSocket connection after 30 seconds if the app goes into the background. The connection remains alive for 30 seconds after backgrounding, but disconnects after that if no pings are received. + + CometChat manual WebSocket handling flow showing manual connect, 30-second background timeout, ping keep alive, optional disconnect, and reconnect flow + + | App State | Behavior | | --- | --- | | App in foreground | Call `CometChat.connect()` to create the WebSocket connection | diff --git a/ui-kit/ios/message-header.mdx b/ui-kit/ios/message-header.mdx index 76e25cdac..9f437d924 100644 --- a/ui-kit/ios/message-header.mdx +++ b/ui-kit/ios/message-header.mdx @@ -569,7 +569,7 @@ messageHeader.set(trailView: { user, group in ``` - CometChatMessageHeader with custom trailView showing video call, voice call, and bookmark buttons + CometChatMessageHeader with custom trailView showing video call, voice call, and bookmark buttons You can create a `CustomTrailView` as a custom `UIView`: diff --git a/ui-kit/react-native/component-styling.mdx b/ui-kit/react-native/component-styling.mdx index 623d8ab16..833c005b8 100644 --- a/ui-kit/react-native/component-styling.mdx +++ b/ui-kit/react-native/component-styling.mdx @@ -1034,7 +1034,7 @@ To learn more about such attributes, refer to the [theme interface](https://gith The `CometChatAIAssistantChatHistory` component displays the history of interactions with an AI assistant. It provides a seamless way to view past conversations, ensuring users can easily reference previous AI responses. - + ```javascript @@ -1346,7 +1346,7 @@ To learn more about such attributes, refer to the [theme interface](https://gith The `CometChatMediaRecorder` component facilitates the recording of audio and video messages. It supports full customization of its recording controls, including button sizes, shapes, and colors, making it an integral part of your media-rich chat experience. - + < src="/images/39d562ac-base_component_media_recorder_styling-a71efb9d9af23f26d7984a97e5ab892e.png" /> ```javascript From 22c0d9dbdc370b4eef5e05a5295c982001b2b984 Mon Sep 17 00:00:00 2001 From: omkar6750 Date: Fri, 8 May 2026 16:56:34 +0530 Subject: [PATCH 09/61] fixed missing alt text in docs --- .../react-native-push-notifications-ios.mdx | 14 +++++++------- ui-kit/react-native/component-styling.mdx | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/notifications/react-native-push-notifications-ios.mdx b/notifications/react-native-push-notifications-ios.mdx index b6637ed42..f5489fc6e 100644 --- a/notifications/react-native-push-notifications-ios.mdx +++ b/notifications/react-native-push-notifications-ios.mdx @@ -67,7 +67,7 @@ For iOS we use Apple Push Notification service (APNs) for both standard and VoIP 1. Open **Keychain Access** → Certificate Assistant → *Request a Certificate From a Certificate Authority*.
- + Apple Developer portal screenshot 2. In **Certificate Information**, enter your Apple Developer email and a common name; choose **Saved to disk**, then **Continue**. 3. Save the CSR file locally—this contains your public/private key pair. @@ -76,31 +76,31 @@ For iOS we use Apple Push Notification service (APNs) for both standard and VoIP 1. Sign in to the [Apple Developer Member Center](https://developer.apple.com/membercenter) → **Certificates, Identifiers & Profiles**.
- + Apple Developer portal screenshot 2. Click **+** to add a certificate.
- + Apple Developer portal screenshot 3. Under **Services**, pick **Apple Push Notification service SSL (Sandbox & Production)**.
- + Apple Developer portal screenshot 4. Select your App ID, upload the CSR, continue, and download the generated `.cer` file.
- + Apple Developer portal screenshot & - + Apple Developer portal screenshot & - + Apple Developer portal screenshot
diff --git a/ui-kit/react-native/component-styling.mdx b/ui-kit/react-native/component-styling.mdx index 833c005b8..771720c16 100644 --- a/ui-kit/react-native/component-styling.mdx +++ b/ui-kit/react-native/component-styling.mdx @@ -1034,7 +1034,7 @@ To learn more about such attributes, refer to the [theme interface](https://gith The `CometChatAIAssistantChatHistory` component displays the history of interactions with an AI assistant. It provides a seamless way to view past conversations, ensuring users can easily reference previous AI responses. - + AI assistant chat history styling preview ```javascript From 244c25eb179e68e814b51eec4b0aac04b8fd9253 Mon Sep 17 00:00:00 2001 From: rajdubey Date: Fri, 8 May 2026 17:10:31 +0530 Subject: [PATCH 10/61] Added migration guide from v4 -> v5 --- .gitignore | 3 +- docs.json | 457 +++-- ui-kit/angular/accessibility.mdx | 80 + ui-kit/angular/ai-features.mdx | 157 +- .../angular/{v5 => }/angular-conversation.mdx | 14 +- .../{v5 => }/angular-one-to-one-chat.mdx | 12 +- .../{v5 => }/angular-tab-based-chat.mdx | 12 +- .../api-reference/chat-state-service.mdx | 24 +- .../formatter-config-service.mdx | 4 +- .../{v5 => }/api-reference/introduction.mdx | 16 +- .../message-bubble-config-service.mdx | 10 +- .../rich-text-editor-service.mdx | 6 +- .../search-conversations-service.mdx | 0 .../api-reference/search-messages-service.mdx | 0 .../api-reference/templates-service.mdx | 0 ui-kit/angular/call-features.mdx | 99 +- ui-kit/angular/components/action-sheet.mdx | 693 ++++++++ ui-kit/angular/components/avatar.mdx | 380 ++++ ui-kit/angular/components/button.mdx | 681 ++++++++ .../components/cometchat-action-bubble.mdx | 0 .../cometchat-ai-assistant-chat-history.mdx | 212 +++ .../cometchat-ai-assistant-chat.mdx | 0 .../cometchat-ai-assistant-message-bubble.mdx | 0 .../components/cometchat-audio-bubble.mdx | 2 +- .../components/cometchat-call-bubble.mdx | 0 .../components/cometchat-call-buttons.mdx | 6 +- .../components/cometchat-call-logs.mdx | 6 +- .../components/cometchat-change-scope.mdx | 373 ++++ .../angular/components/cometchat-checkbox.mdx | 697 ++++++++ ...ometchat-collaborative-document-bubble.mdx | 2 +- ...etchat-collaborative-whiteboard-bubble.mdx | 2 +- .../cometchat-conversation-item.mdx | 0 .../cometchat-conversation-starter.mdx | 4 +- .../cometchat-conversation-summary.mdx | 0 .../components/cometchat-conversations.mdx | 2 +- .../components/cometchat-create-poll.mdx | 167 ++ .../components/cometchat-delete-bubble.mdx | 0 .../angular/components/cometchat-dropdown.mdx | 856 +++++++++ .../components/cometchat-error-boundary.mdx | 133 ++ .../components/cometchat-file-bubble.mdx | 2 +- .../cometchat-flag-message-dialog.mdx | 148 ++ .../cometchat-fullscreen-viewer.mdx | 1542 +++++++++++++++++ .../components/cometchat-group-item.mdx | 4 +- .../cometchat-group-member-item.mdx | 4 +- .../components/cometchat-group-members.mdx | 4 +- .../{v5 => }/components/cometchat-groups.mdx | 2 +- .../components/cometchat-image-bubble.mdx | 2 +- .../components/cometchat-incoming-call.mdx | 4 +- .../components/cometchat-link-dialog.mdx | 174 ++ .../components/cometchat-link-popover.mdx | 140 ++ .../cometchat-markdown-renderer.mdx | 250 +++ .../components/cometchat-media-recorder.mdx | 433 +++++ .../components/cometchat-message-bubble.mdx | 18 +- .../components/cometchat-message-composer.mdx | 4 +- .../components/cometchat-message-header.mdx | 6 +- .../cometchat-message-information.mdx | 8 +- .../components/cometchat-message-list.mdx | 2 +- .../components/cometchat-ongoing-call.mdx | 65 + .../components/cometchat-outgoing-call.mdx | 2 +- .../components/cometchat-paginated-list.mdx | 856 +++++++++ .../components/cometchat-poll-bubble.mdx | 0 .../angular/components/cometchat-popover.mdx | 593 +++++++ .../components/cometchat-radio-button.mdx | 806 +++++++++ .../components/cometchat-reaction-info.mdx | 6 +- .../components/cometchat-reaction-list.mdx | 8 +- .../components/cometchat-reactions.mdx | 8 +- .../components/cometchat-search-bar.mdx | 409 +++++ .../{v5 => }/components/cometchat-search.mdx | 6 +- .../components/cometchat-smart-replies.mdx | 4 +- .../components/cometchat-sticker-bubble.mdx | 0 .../cometchat-stickers-keyboard.mdx | 6 +- .../cometchat-stream-message-bubble.mdx | 71 + .../components/cometchat-text-bubble.mdx | 2 +- .../components/cometchat-thread-header.mdx | 6 +- .../components/cometchat-thread-view.mdx | 6 +- ui-kit/angular/components/cometchat-toast.mdx | 1112 ++++++++++++ .../cometchat-toolcall-argument-bubble.mdx | 54 + .../cometchat-toolcall-result-bubble.mdx | 60 + .../components/cometchat-typing-indicator.mdx | 167 ++ .../components/cometchat-user-item.mdx | 4 +- .../{v5 => }/components/cometchat-users.mdx | 2 +- .../components/cometchat-video-bubble.mdx | 2 +- ui-kit/angular/components/confirm-dialog.mdx | 885 ++++++++++ ui-kit/angular/components/context-menu.mdx | 894 ++++++++++ ui-kit/angular/components/date.mdx | 714 ++++++++ ui-kit/angular/components/emoji-keyboard.mdx | 335 ++++ ui-kit/angular/components/list-item.mdx | 824 +++++++++ ui-kit/angular/components/message-preview.mdx | 857 +++++++++ .../angular/{v5 => }/components/overview.mdx | 44 +- ui-kit/angular/core-features.mdx | 239 ++- .../customization/date-time-formatting.mdx | 281 +++ .../{v5 => }/customization/global-config.mdx | 14 +- .../{v5 => }/customization/localization.mdx | 2 +- .../angular/customization/migration-guide.mdx | 283 +++ .../{v5 => }/customization/theming.mdx | 0 ui-kit/angular/events.mdx | 248 ++- ui-kit/angular/extensions.mdx | 175 +- .../{v5 => }/guides/block-unblock-user.mdx | 10 +- .../{v5 => }/guides/call-log-details.mdx | 15 +- .../{v5 => }/guides/custom-message-types.mdx | 2 +- .../angular/guides/custom-text-formatter.mdx | 296 ++++ ui-kit/angular/{v5 => }/guides/group-chat.mdx | 44 +- .../{v5 => }/guides/hashtag-formatter.mdx | 14 +- .../{v5 => }/guides/mentions-formatter.mdx | 53 +- .../{v5 => }/guides/message-privately.mdx | 10 +- ui-kit/angular/{v5 => }/guides/new-chat.mdx | 10 +- ui-kit/angular/guides/overview.mdx | 65 + .../{v5 => }/guides/rich-text-formatting.mdx | 4 +- .../{v5 => }/guides/search-messages.mdx | 23 +- .../{v5 => }/guides/shortcut-formatter.mdx | 134 +- .../{v5 => }/guides/state-management.mdx | 18 +- .../{v5 => }/guides/subscription-patterns.mdx | 0 .../{v5 => }/guides/threaded-messages.mdx | 18 +- .../angular/{v5 => }/guides/url-formatter.mdx | 51 +- ui-kit/angular/{v5 => }/integration.mdx | 20 +- ui-kit/angular/methods.mdx | 663 ++----- ui-kit/angular/overview.mdx | 123 +- ui-kit/angular/{v5 => }/quickstart.mdx | 6 +- ui-kit/angular/sound-manager.mdx | 93 +- ui-kit/angular/{v5 => }/troubleshooting.mdx | 8 +- ui-kit/angular/{ => v4}/action-sheet.mdx | 0 ui-kit/angular/v4/ai-features.mdx | 60 + ui-kit/angular/{ => v4}/audio-bubble.mdx | 0 ui-kit/angular/{ => v4}/avatar.mdx | 0 ui-kit/angular/{ => v4}/backdrop.mdx | 0 ui-kit/angular/{ => v4}/badge.mdx | 0 ui-kit/angular/{ => v4}/button-group.mdx | 0 ui-kit/angular/{ => v4}/call-buttons.mdx | 0 ui-kit/angular/v4/call-features.mdx | 66 + ui-kit/angular/{ => v4}/call-log-details.mdx | 0 ui-kit/angular/{ => v4}/call-log-history.mdx | 0 .../{ => v4}/call-log-participants.mdx | 0 .../angular/{ => v4}/call-log-recording.mdx | 0 .../{ => v4}/call-log-with-details.mdx | 0 ui-kit/angular/{ => v4}/call-logs.mdx | 0 ui-kit/angular/{ => v4}/call-overview.mdx | 0 ui-kit/angular/{ => v4}/checkbox.mdx | 0 .../angular/{ => v4}/cometchat-quick-view.mdx | 0 .../angular/{ => v4}/components-overview.mdx | 0 ui-kit/angular/{ => v4}/confirm-dialog.mdx | 0 ui-kit/angular/{ => v4}/contacts.mdx | 0 .../{ => v4}/conversations-with-messages.mdx | 0 ui-kit/angular/{ => v4}/conversations.mdx | 0 ui-kit/angular/v4/core-features.mdx | 131 ++ ui-kit/angular/{ => v4}/create-group.mdx | 0 .../angular/{ => v4}/custom-message-guide.mdx | 0 .../{ => v4}/custom-text-formatter-guide.mdx | 0 ui-kit/angular/{ => v4}/date.mdx | 0 ui-kit/angular/{ => v4}/document-bubble.mdx | 0 ui-kit/angular/{ => v4}/dropdown.mdx | 0 ui-kit/angular/{ => v4}/emoji-keyboard.mdx | 0 ui-kit/angular/v4/events.mdx | 100 ++ ui-kit/angular/v4/extensions.mdx | 132 ++ ui-kit/angular/{ => v4}/file-bubble.mdx | 0 ui-kit/angular/{ => v4}/getting-started.mdx | 0 ui-kit/angular/{ => v4}/group-add-members.mdx | 0 .../angular/{ => v4}/group-banned-members.mdx | 0 ui-kit/angular/{ => v4}/group-details.mdx | 0 ui-kit/angular/{ => v4}/group-members.mdx | 0 .../{ => v4}/group-transfer-ownership.mdx | 0 .../angular/{ => v4}/groups-with-messages.mdx | 0 ui-kit/angular/{ => v4}/groups.mdx | 0 ui-kit/angular/{ => v4}/icon-button.mdx | 0 ui-kit/angular/{ => v4}/icon.mdx | 0 ui-kit/angular/{ => v4}/image-bubble.mdx | 0 ui-kit/angular/{ => v4}/incoming-call.mdx | 0 ui-kit/angular/{ => v4}/input.mdx | 0 .../{ => v4}/interactive-action-entity.mdx | 0 .../{ => v4}/interactive-button-element.mdx | 0 .../{ => v4}/interactive-card-bubble.mdx | 0 .../{ => v4}/interactive-card-message.mdx | 0 .../{ => v4}/interactive-checkbox-element.mdx | 0 ...interactive-custom-interactive-message.mdx | 0 .../interactive-date-time-picker-element.mdx | 0 .../{ => v4}/interactive-dropdown-element.mdx | 0 .../{ => v4}/interactive-element-type.mdx | 0 .../{ => v4}/interactive-form-bubble.mdx | 0 .../{ => v4}/interactive-form-message.mdx | 0 .../{ => v4}/interactive-label-element.mdx | 0 .../interactive-radio-button-element.mdx | 0 .../{ => v4}/interactive-scheduler-bubble.mdx | 0 .../interactive-scheduler-message.mdx | 0 .../interactive-single-select-element.mdx | 0 .../interactive-text-input-element.mdx | 0 .../angular/{ => v4}/join-protected-group.mdx | 0 ui-kit/angular/{ => v4}/label.mdx | 0 ui-kit/angular/{ => v4}/link/changelog.mdx | 0 ui-kit/angular/{ => v4}/link/sample.mdx | 0 ui-kit/angular/{ => v4}/list-item.mdx | 0 ui-kit/angular/{ => v4}/list.mdx | 0 ui-kit/angular/{ => v4}/loader.mdx | 0 ui-kit/angular/{ => v4}/localize.mdx | 0 ui-kit/angular/{ => v4}/media-recorder.mdx | 0 .../{ => v4}/mentions-formatter-guide.mdx | 0 ui-kit/angular/{ => v4}/message-bubble.mdx | 0 ui-kit/angular/{ => v4}/message-composer.mdx | 0 ui-kit/angular/{ => v4}/message-header.mdx | 0 .../angular/{ => v4}/message-information.mdx | 0 ui-kit/angular/{ => v4}/message-input.mdx | 0 ui-kit/angular/{ => v4}/message-list.mdx | 0 ui-kit/angular/{ => v4}/message-template.mdx | 0 ui-kit/angular/{ => v4}/messages.mdx | 0 ui-kit/angular/v4/methods.mdx | 659 +++++++ ui-kit/angular/{ => v4}/modal.mdx | 0 .../{ => v4}/multi-tab-chat-ui-guide.mdx | 0 .../{ => v4}/new-attachment-option-guide.mdx | 0 .../{ => v4}/new-message-option-guide.mdx | 0 ui-kit/angular/{ => v4}/ongoing-call.mdx | 0 ui-kit/angular/{ => v4}/outgoing-call.mdx | 0 ui-kit/angular/v4/overview.mdx | 67 + ui-kit/angular/{ => v4}/pop-over.mdx | 0 ui-kit/angular/{ => v4}/radio-button.mdx | 0 ui-kit/angular/{ => v4}/reaction-info.mdx | 0 ui-kit/angular/{ => v4}/reaction-list.mdx | 0 ui-kit/angular/{ => v4}/reaction.mdx | 0 ui-kit/angular/{ => v4}/receipt.mdx | 0 ui-kit/angular/{ => v4}/search-input.mdx | 0 .../{ => v4}/shortcut-formatter-guide.mdx | 0 ui-kit/angular/{ => v4}/singleselect.mdx | 0 ui-kit/angular/v4/sound-manager.mdx | 63 + ui-kit/angular/{ => v4}/status-indicator.mdx | 0 ui-kit/angular/{ => v4}/text-bubble.mdx | 0 ui-kit/angular/{ => v4}/theme.mdx | 0 ui-kit/angular/{ => v4}/threaded-messages.mdx | 0 .../angular/{ => v4}/url-formatter-guide.mdx | 0 ui-kit/angular/{ => v4}/user-details.mdx | 0 .../angular/{ => v4}/user-member-wrapper.mdx | 0 .../angular/{ => v4}/users-with-messages.mdx | 0 ui-kit/angular/{ => v4}/users.mdx | 0 ui-kit/angular/{ => v4}/video-bubble.mdx | 0 ui-kit/angular/v5/accessibility.mdx | 80 - ui-kit/angular/v5/ai-features.mdx | 157 -- ui-kit/angular/v5/call-features.mdx | 109 -- ui-kit/angular/v5/core-features.mdx | 149 -- .../v5/customization/migration-guide.mdx | 1499 ---------------- ui-kit/angular/v5/events.mdx | 168 -- ui-kit/angular/v5/extensions.mdx | 185 -- .../v5/guides/custom-text-formatter.mdx | 331 ---- ui-kit/angular/v5/guides/overview.mdx | 65 - ui-kit/angular/v5/installation.mdx | 58 - ui-kit/angular/v5/methods.mdx | 282 --- ui-kit/angular/v5/overview.mdx | 92 - ui-kit/angular/v5/sound-manager.mdx | 84 - 243 files changed, 19369 insertions(+), 4755 deletions(-) create mode 100644 ui-kit/angular/accessibility.mdx rename ui-kit/angular/{v5 => }/angular-conversation.mdx (95%) rename ui-kit/angular/{v5 => }/angular-one-to-one-chat.mdx (96%) rename ui-kit/angular/{v5 => }/angular-tab-based-chat.mdx (96%) rename ui-kit/angular/{v5 => }/api-reference/chat-state-service.mdx (87%) rename ui-kit/angular/{v5 => }/api-reference/formatter-config-service.mdx (98%) rename ui-kit/angular/{v5 => }/api-reference/introduction.mdx (88%) rename ui-kit/angular/{v5 => }/api-reference/message-bubble-config-service.mdx (93%) rename ui-kit/angular/{v5 => }/api-reference/rich-text-editor-service.mdx (97%) rename ui-kit/angular/{v5 => }/api-reference/search-conversations-service.mdx (100%) rename ui-kit/angular/{v5 => }/api-reference/search-messages-service.mdx (100%) rename ui-kit/angular/{v5 => }/api-reference/templates-service.mdx (100%) create mode 100644 ui-kit/angular/components/action-sheet.mdx create mode 100644 ui-kit/angular/components/avatar.mdx create mode 100644 ui-kit/angular/components/button.mdx rename ui-kit/angular/{v5 => }/components/cometchat-action-bubble.mdx (100%) create mode 100644 ui-kit/angular/components/cometchat-ai-assistant-chat-history.mdx rename ui-kit/angular/{v5 => }/components/cometchat-ai-assistant-chat.mdx (100%) rename ui-kit/angular/{v5 => }/components/cometchat-ai-assistant-message-bubble.mdx (100%) rename ui-kit/angular/{v5 => }/components/cometchat-audio-bubble.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-call-bubble.mdx (100%) rename ui-kit/angular/{v5 => }/components/cometchat-call-buttons.mdx (94%) rename ui-kit/angular/{v5 => }/components/cometchat-call-logs.mdx (98%) create mode 100644 ui-kit/angular/components/cometchat-change-scope.mdx create mode 100644 ui-kit/angular/components/cometchat-checkbox.mdx rename ui-kit/angular/{v5 => }/components/cometchat-collaborative-document-bubble.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-collaborative-whiteboard-bubble.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-conversation-item.mdx (100%) rename ui-kit/angular/{v5 => }/components/cometchat-conversation-starter.mdx (94%) rename ui-kit/angular/{v5 => }/components/cometchat-conversation-summary.mdx (100%) rename ui-kit/angular/{v5 => }/components/cometchat-conversations.mdx (99%) create mode 100644 ui-kit/angular/components/cometchat-create-poll.mdx rename ui-kit/angular/{v5 => }/components/cometchat-delete-bubble.mdx (100%) create mode 100644 ui-kit/angular/components/cometchat-dropdown.mdx create mode 100644 ui-kit/angular/components/cometchat-error-boundary.mdx rename ui-kit/angular/{v5 => }/components/cometchat-file-bubble.mdx (99%) create mode 100644 ui-kit/angular/components/cometchat-flag-message-dialog.mdx create mode 100644 ui-kit/angular/components/cometchat-fullscreen-viewer.mdx rename ui-kit/angular/{v5 => }/components/cometchat-group-item.mdx (96%) rename ui-kit/angular/{v5 => }/components/cometchat-group-member-item.mdx (97%) rename ui-kit/angular/{v5 => }/components/cometchat-group-members.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-groups.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-image-bubble.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-incoming-call.mdx (96%) create mode 100644 ui-kit/angular/components/cometchat-link-dialog.mdx create mode 100644 ui-kit/angular/components/cometchat-link-popover.mdx create mode 100644 ui-kit/angular/components/cometchat-markdown-renderer.mdx create mode 100644 ui-kit/angular/components/cometchat-media-recorder.mdx rename ui-kit/angular/{v5 => }/components/cometchat-message-bubble.mdx (98%) rename ui-kit/angular/{v5 => }/components/cometchat-message-composer.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-message-header.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-message-information.mdx (93%) rename ui-kit/angular/{v5 => }/components/cometchat-message-list.mdx (99%) create mode 100644 ui-kit/angular/components/cometchat-ongoing-call.mdx rename ui-kit/angular/{v5 => }/components/cometchat-outgoing-call.mdx (97%) create mode 100644 ui-kit/angular/components/cometchat-paginated-list.mdx rename ui-kit/angular/{v5 => }/components/cometchat-poll-bubble.mdx (100%) create mode 100644 ui-kit/angular/components/cometchat-popover.mdx create mode 100644 ui-kit/angular/components/cometchat-radio-button.mdx rename ui-kit/angular/{v5 => }/components/cometchat-reaction-info.mdx (92%) rename ui-kit/angular/{v5 => }/components/cometchat-reaction-list.mdx (96%) rename ui-kit/angular/{v5 => }/components/cometchat-reactions.mdx (93%) create mode 100644 ui-kit/angular/components/cometchat-search-bar.mdx rename ui-kit/angular/{v5 => }/components/cometchat-search.mdx (98%) rename ui-kit/angular/{v5 => }/components/cometchat-smart-replies.mdx (95%) rename ui-kit/angular/{v5 => }/components/cometchat-sticker-bubble.mdx (100%) rename ui-kit/angular/{v5 => }/components/cometchat-stickers-keyboard.mdx (93%) create mode 100644 ui-kit/angular/components/cometchat-stream-message-bubble.mdx rename ui-kit/angular/{v5 => }/components/cometchat-text-bubble.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-thread-header.mdx (95%) rename ui-kit/angular/{v5 => }/components/cometchat-thread-view.mdx (93%) create mode 100644 ui-kit/angular/components/cometchat-toast.mdx create mode 100644 ui-kit/angular/components/cometchat-toolcall-argument-bubble.mdx create mode 100644 ui-kit/angular/components/cometchat-toolcall-result-bubble.mdx create mode 100644 ui-kit/angular/components/cometchat-typing-indicator.mdx rename ui-kit/angular/{v5 => }/components/cometchat-user-item.mdx (96%) rename ui-kit/angular/{v5 => }/components/cometchat-users.mdx (99%) rename ui-kit/angular/{v5 => }/components/cometchat-video-bubble.mdx (99%) create mode 100644 ui-kit/angular/components/confirm-dialog.mdx create mode 100644 ui-kit/angular/components/context-menu.mdx create mode 100644 ui-kit/angular/components/date.mdx create mode 100644 ui-kit/angular/components/emoji-keyboard.mdx create mode 100644 ui-kit/angular/components/list-item.mdx create mode 100644 ui-kit/angular/components/message-preview.mdx rename ui-kit/angular/{v5 => }/components/overview.mdx (83%) create mode 100644 ui-kit/angular/customization/date-time-formatting.mdx rename ui-kit/angular/{v5 => }/customization/global-config.mdx (88%) rename ui-kit/angular/{v5 => }/customization/localization.mdx (98%) create mode 100644 ui-kit/angular/customization/migration-guide.mdx rename ui-kit/angular/{v5 => }/customization/theming.mdx (100%) rename ui-kit/angular/{v5 => }/guides/block-unblock-user.mdx (95%) rename ui-kit/angular/{v5 => }/guides/call-log-details.mdx (94%) rename ui-kit/angular/{v5 => }/guides/custom-message-types.mdx (98%) create mode 100644 ui-kit/angular/guides/custom-text-formatter.mdx rename ui-kit/angular/{v5 => }/guides/group-chat.mdx (89%) rename ui-kit/angular/{v5 => }/guides/hashtag-formatter.mdx (95%) rename ui-kit/angular/{v5 => }/guides/mentions-formatter.mdx (68%) rename ui-kit/angular/{v5 => }/guides/message-privately.mdx (95%) rename ui-kit/angular/{v5 => }/guides/new-chat.mdx (95%) create mode 100644 ui-kit/angular/guides/overview.mdx rename ui-kit/angular/{v5 => }/guides/rich-text-formatting.mdx (99%) rename ui-kit/angular/{v5 => }/guides/search-messages.mdx (88%) rename ui-kit/angular/{v5 => }/guides/shortcut-formatter.mdx (55%) rename ui-kit/angular/{v5 => }/guides/state-management.mdx (92%) rename ui-kit/angular/{v5 => }/guides/subscription-patterns.mdx (100%) rename ui-kit/angular/{v5 => }/guides/threaded-messages.mdx (92%) rename ui-kit/angular/{v5 => }/guides/url-formatter.mdx (63%) rename ui-kit/angular/{v5 => }/integration.mdx (94%) rename ui-kit/angular/{v5 => }/quickstart.mdx (96%) rename ui-kit/angular/{v5 => }/troubleshooting.mdx (99%) rename ui-kit/angular/{ => v4}/action-sheet.mdx (100%) create mode 100644 ui-kit/angular/v4/ai-features.mdx rename ui-kit/angular/{ => v4}/audio-bubble.mdx (100%) rename ui-kit/angular/{ => v4}/avatar.mdx (100%) rename ui-kit/angular/{ => v4}/backdrop.mdx (100%) rename ui-kit/angular/{ => v4}/badge.mdx (100%) rename ui-kit/angular/{ => v4}/button-group.mdx (100%) rename ui-kit/angular/{ => v4}/call-buttons.mdx (100%) create mode 100644 ui-kit/angular/v4/call-features.mdx rename ui-kit/angular/{ => v4}/call-log-details.mdx (100%) rename ui-kit/angular/{ => v4}/call-log-history.mdx (100%) rename ui-kit/angular/{ => v4}/call-log-participants.mdx (100%) rename ui-kit/angular/{ => v4}/call-log-recording.mdx (100%) rename ui-kit/angular/{ => v4}/call-log-with-details.mdx (100%) rename ui-kit/angular/{ => v4}/call-logs.mdx (100%) rename ui-kit/angular/{ => v4}/call-overview.mdx (100%) rename ui-kit/angular/{ => v4}/checkbox.mdx (100%) rename ui-kit/angular/{ => v4}/cometchat-quick-view.mdx (100%) rename ui-kit/angular/{ => v4}/components-overview.mdx (100%) rename ui-kit/angular/{ => v4}/confirm-dialog.mdx (100%) rename ui-kit/angular/{ => v4}/contacts.mdx (100%) rename ui-kit/angular/{ => v4}/conversations-with-messages.mdx (100%) rename ui-kit/angular/{ => v4}/conversations.mdx (100%) create mode 100644 ui-kit/angular/v4/core-features.mdx rename ui-kit/angular/{ => v4}/create-group.mdx (100%) rename ui-kit/angular/{ => v4}/custom-message-guide.mdx (100%) rename ui-kit/angular/{ => v4}/custom-text-formatter-guide.mdx (100%) rename ui-kit/angular/{ => v4}/date.mdx (100%) rename ui-kit/angular/{ => v4}/document-bubble.mdx (100%) rename ui-kit/angular/{ => v4}/dropdown.mdx (100%) rename ui-kit/angular/{ => v4}/emoji-keyboard.mdx (100%) create mode 100644 ui-kit/angular/v4/events.mdx create mode 100644 ui-kit/angular/v4/extensions.mdx rename ui-kit/angular/{ => v4}/file-bubble.mdx (100%) rename ui-kit/angular/{ => v4}/getting-started.mdx (100%) rename ui-kit/angular/{ => v4}/group-add-members.mdx (100%) rename ui-kit/angular/{ => v4}/group-banned-members.mdx (100%) rename ui-kit/angular/{ => v4}/group-details.mdx (100%) rename ui-kit/angular/{ => v4}/group-members.mdx (100%) rename ui-kit/angular/{ => v4}/group-transfer-ownership.mdx (100%) rename ui-kit/angular/{ => v4}/groups-with-messages.mdx (100%) rename ui-kit/angular/{ => v4}/groups.mdx (100%) rename ui-kit/angular/{ => v4}/icon-button.mdx (100%) rename ui-kit/angular/{ => v4}/icon.mdx (100%) rename ui-kit/angular/{ => v4}/image-bubble.mdx (100%) rename ui-kit/angular/{ => v4}/incoming-call.mdx (100%) rename ui-kit/angular/{ => v4}/input.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-action-entity.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-button-element.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-card-bubble.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-card-message.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-checkbox-element.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-custom-interactive-message.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-date-time-picker-element.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-dropdown-element.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-element-type.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-form-bubble.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-form-message.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-label-element.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-radio-button-element.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-scheduler-bubble.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-scheduler-message.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-single-select-element.mdx (100%) rename ui-kit/angular/{ => v4}/interactive-text-input-element.mdx (100%) rename ui-kit/angular/{ => v4}/join-protected-group.mdx (100%) rename ui-kit/angular/{ => v4}/label.mdx (100%) rename ui-kit/angular/{ => v4}/link/changelog.mdx (100%) rename ui-kit/angular/{ => v4}/link/sample.mdx (100%) rename ui-kit/angular/{ => v4}/list-item.mdx (100%) rename ui-kit/angular/{ => v4}/list.mdx (100%) rename ui-kit/angular/{ => v4}/loader.mdx (100%) rename ui-kit/angular/{ => v4}/localize.mdx (100%) rename ui-kit/angular/{ => v4}/media-recorder.mdx (100%) rename ui-kit/angular/{ => v4}/mentions-formatter-guide.mdx (100%) rename ui-kit/angular/{ => v4}/message-bubble.mdx (100%) rename ui-kit/angular/{ => v4}/message-composer.mdx (100%) rename ui-kit/angular/{ => v4}/message-header.mdx (100%) rename ui-kit/angular/{ => v4}/message-information.mdx (100%) rename ui-kit/angular/{ => v4}/message-input.mdx (100%) rename ui-kit/angular/{ => v4}/message-list.mdx (100%) rename ui-kit/angular/{ => v4}/message-template.mdx (100%) rename ui-kit/angular/{ => v4}/messages.mdx (100%) create mode 100644 ui-kit/angular/v4/methods.mdx rename ui-kit/angular/{ => v4}/modal.mdx (100%) rename ui-kit/angular/{ => v4}/multi-tab-chat-ui-guide.mdx (100%) rename ui-kit/angular/{ => v4}/new-attachment-option-guide.mdx (100%) rename ui-kit/angular/{ => v4}/new-message-option-guide.mdx (100%) rename ui-kit/angular/{ => v4}/ongoing-call.mdx (100%) rename ui-kit/angular/{ => v4}/outgoing-call.mdx (100%) create mode 100644 ui-kit/angular/v4/overview.mdx rename ui-kit/angular/{ => v4}/pop-over.mdx (100%) rename ui-kit/angular/{ => v4}/radio-button.mdx (100%) rename ui-kit/angular/{ => v4}/reaction-info.mdx (100%) rename ui-kit/angular/{ => v4}/reaction-list.mdx (100%) rename ui-kit/angular/{ => v4}/reaction.mdx (100%) rename ui-kit/angular/{ => v4}/receipt.mdx (100%) rename ui-kit/angular/{ => v4}/search-input.mdx (100%) rename ui-kit/angular/{ => v4}/shortcut-formatter-guide.mdx (100%) rename ui-kit/angular/{ => v4}/singleselect.mdx (100%) create mode 100644 ui-kit/angular/v4/sound-manager.mdx rename ui-kit/angular/{ => v4}/status-indicator.mdx (100%) rename ui-kit/angular/{ => v4}/text-bubble.mdx (100%) rename ui-kit/angular/{ => v4}/theme.mdx (100%) rename ui-kit/angular/{ => v4}/threaded-messages.mdx (100%) rename ui-kit/angular/{ => v4}/url-formatter-guide.mdx (100%) rename ui-kit/angular/{ => v4}/user-details.mdx (100%) rename ui-kit/angular/{ => v4}/user-member-wrapper.mdx (100%) rename ui-kit/angular/{ => v4}/users-with-messages.mdx (100%) rename ui-kit/angular/{ => v4}/users.mdx (100%) rename ui-kit/angular/{ => v4}/video-bubble.mdx (100%) delete mode 100644 ui-kit/angular/v5/accessibility.mdx delete mode 100644 ui-kit/angular/v5/ai-features.mdx delete mode 100644 ui-kit/angular/v5/call-features.mdx delete mode 100644 ui-kit/angular/v5/core-features.mdx delete mode 100644 ui-kit/angular/v5/customization/migration-guide.mdx delete mode 100644 ui-kit/angular/v5/events.mdx delete mode 100644 ui-kit/angular/v5/extensions.mdx delete mode 100644 ui-kit/angular/v5/guides/custom-text-formatter.mdx delete mode 100644 ui-kit/angular/v5/guides/overview.mdx delete mode 100644 ui-kit/angular/v5/installation.mdx delete mode 100644 ui-kit/angular/v5/methods.mdx delete mode 100644 ui-kit/angular/v5/overview.mdx delete mode 100644 ui-kit/angular/v5/sound-manager.mdx diff --git a/.gitignore b/.gitignore index a5cddc674..78399d61b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,4 @@ __pycache__/ /prompts /docs-comparison-tool /node_modules -/legacy-docs -/ui-kit/angular-dev-v5-docs \ No newline at end of file +/legacy-docs \ No newline at end of file diff --git a/docs.json b/docs.json index 2ec150550..c4b1610f7 100644 --- a/docs.json +++ b/docs.json @@ -2402,16 +2402,15 @@ { "group": " ", "pages": [ - "ui-kit/angular/v5/overview", + "ui-kit/angular/overview", { "group": "Getting Started", "pages": [ - "ui-kit/angular/v5/installation", - "ui-kit/angular/v5/quickstart", - "ui-kit/angular/v5/integration", - "ui-kit/angular/v5/angular-conversation", - "ui-kit/angular/v5/angular-one-to-one-chat", - "ui-kit/angular/v5/angular-tab-based-chat" + "ui-kit/angular/quickstart", + "ui-kit/angular/integration", + "ui-kit/angular/angular-conversation", + "ui-kit/angular/angular-one-to-one-chat", + "ui-kit/angular/angular-tab-based-chat" ] }, { @@ -2420,178 +2419,178 @@ { "group": "Chat", "pages": [ - "ui-kit/angular/v5/core-features", - "ui-kit/angular/v5/extensions", - "ui-kit/angular/v5/ai-features" + "ui-kit/angular/core-features", + "ui-kit/angular/extensions", + "ui-kit/angular/ai-features" ] }, - "ui-kit/angular/v5/call-features" + "ui-kit/angular/call-features" ] }, { "group": "Theming", "pages": [ - "ui-kit/angular/v5/customization/theming", - "ui-kit/angular/v5/customization/localization", - "ui-kit/angular/v5/customization/global-config", - "ui-kit/angular/v5/customization/date-time-formatting", - "ui-kit/angular/v5/sound-manager" + "ui-kit/angular/customization/theming", + "ui-kit/angular/customization/localization", + "ui-kit/angular/customization/global-config", + "ui-kit/angular/customization/date-time-formatting", + "ui-kit/angular/sound-manager" ] }, { "group": "Components", "pages": [ - "ui-kit/angular/v5/components/overview", + "ui-kit/angular/components/overview", { "group": "Conversations", "pages": [ - "ui-kit/angular/v5/components/cometchat-conversations", - "ui-kit/angular/v5/components/cometchat-conversation-item" + "ui-kit/angular/components/cometchat-conversations", + "ui-kit/angular/components/cometchat-conversation-item" ] }, { "group": "Users", "pages": [ - "ui-kit/angular/v5/components/cometchat-users", - "ui-kit/angular/v5/components/cometchat-user-item" + "ui-kit/angular/components/cometchat-users", + "ui-kit/angular/components/cometchat-user-item" ] }, { "group": "Groups", "pages": [ - "ui-kit/angular/v5/components/cometchat-groups", - "ui-kit/angular/v5/components/cometchat-group-item" + "ui-kit/angular/components/cometchat-groups", + "ui-kit/angular/components/cometchat-group-item" ] }, { "group": "Group Members", "pages": [ - "ui-kit/angular/v5/components/cometchat-group-members", - "ui-kit/angular/v5/components/cometchat-group-member-item" + "ui-kit/angular/components/cometchat-group-members", + "ui-kit/angular/components/cometchat-group-member-item" ] }, { "group": "Message Header", "pages": [ - "ui-kit/angular/v5/components/cometchat-message-header" + "ui-kit/angular/components/cometchat-message-header" ] }, { "group": "Search", "pages": [ - "ui-kit/angular/v5/components/cometchat-search" + "ui-kit/angular/components/cometchat-search" ] }, { "group": "Message List", "pages": [ - "ui-kit/angular/v5/components/cometchat-message-list", - "ui-kit/angular/v5/components/cometchat-flag-message-dialog", - "ui-kit/angular/v5/components/cometchat-typing-indicator" + "ui-kit/angular/components/cometchat-message-list", + "ui-kit/angular/components/cometchat-flag-message-dialog", + "ui-kit/angular/components/cometchat-typing-indicator" ] }, { "group": "Message Composer", "pages": [ - "ui-kit/angular/v5/components/cometchat-message-composer" + "ui-kit/angular/components/cometchat-message-composer" ] }, { "group": "Message Bubble", "pages": [ - "ui-kit/angular/v5/components/cometchat-message-bubble", - "ui-kit/angular/v5/components/cometchat-paginated-list" + "ui-kit/angular/components/cometchat-message-bubble", + "ui-kit/angular/components/cometchat-paginated-list" ] }, { "group": "Message Information", "pages": [ - "ui-kit/angular/v5/components/cometchat-message-information" + "ui-kit/angular/components/cometchat-message-information" ] }, { "group": "Thread Header", "pages": [ - "ui-kit/angular/v5/components/cometchat-thread-header", - "ui-kit/angular/v5/components/cometchat-thread-view" + "ui-kit/angular/components/cometchat-thread-header", + "ui-kit/angular/components/cometchat-thread-view" ] }, { "group": "Message Bubbles", "pages": [ - "ui-kit/angular/v5/components/cometchat-text-bubble", - "ui-kit/angular/v5/components/cometchat-image-bubble", - "ui-kit/angular/v5/components/cometchat-video-bubble", - "ui-kit/angular/v5/components/cometchat-audio-bubble", - "ui-kit/angular/v5/components/cometchat-file-bubble", - "ui-kit/angular/v5/components/cometchat-action-bubble", - "ui-kit/angular/v5/components/cometchat-delete-bubble", - "ui-kit/angular/v5/components/cometchat-call-bubble", - "ui-kit/angular/v5/components/cometchat-poll-bubble", - "ui-kit/angular/v5/components/cometchat-create-poll", - "ui-kit/angular/v5/components/cometchat-collaborative-document-bubble", - "ui-kit/angular/v5/components/cometchat-collaborative-whiteboard-bubble", - "ui-kit/angular/v5/components/cometchat-sticker-bubble", - "ui-kit/angular/v5/components/cometchat-stickers-keyboard" + "ui-kit/angular/components/cometchat-text-bubble", + "ui-kit/angular/components/cometchat-image-bubble", + "ui-kit/angular/components/cometchat-video-bubble", + "ui-kit/angular/components/cometchat-audio-bubble", + "ui-kit/angular/components/cometchat-file-bubble", + "ui-kit/angular/components/cometchat-action-bubble", + "ui-kit/angular/components/cometchat-delete-bubble", + "ui-kit/angular/components/cometchat-call-bubble", + "ui-kit/angular/components/cometchat-poll-bubble", + "ui-kit/angular/components/cometchat-create-poll", + "ui-kit/angular/components/cometchat-collaborative-document-bubble", + "ui-kit/angular/components/cometchat-collaborative-whiteboard-bubble", + "ui-kit/angular/components/cometchat-sticker-bubble", + "ui-kit/angular/components/cometchat-stickers-keyboard" ] }, { "group": "Reactions", "pages": [ - "ui-kit/angular/v5/components/cometchat-reactions", - "ui-kit/angular/v5/components/cometchat-reaction-list", - "ui-kit/angular/v5/components/cometchat-reaction-info" + "ui-kit/angular/components/cometchat-reactions", + "ui-kit/angular/components/cometchat-reaction-list", + "ui-kit/angular/components/cometchat-reaction-info" ] }, { "group": "Calls", "pages": [ - "ui-kit/angular/v5/components/cometchat-call-buttons", - "ui-kit/angular/v5/components/cometchat-incoming-call", - "ui-kit/angular/v5/components/cometchat-outgoing-call", - "ui-kit/angular/v5/components/cometchat-ongoing-call", - "ui-kit/angular/v5/components/cometchat-call-logs" + "ui-kit/angular/components/cometchat-call-buttons", + "ui-kit/angular/components/cometchat-incoming-call", + "ui-kit/angular/components/cometchat-outgoing-call", + "ui-kit/angular/components/cometchat-ongoing-call", + "ui-kit/angular/components/cometchat-call-logs" ] }, { "group": "AI", "pages": [ - "ui-kit/angular/v5/components/cometchat-smart-replies", - "ui-kit/angular/v5/components/cometchat-conversation-starter", - "ui-kit/angular/v5/components/cometchat-conversation-summary", - "ui-kit/angular/v5/components/cometchat-ai-assistant-chat", - "ui-kit/angular/v5/components/cometchat-ai-assistant-chat-history", - "ui-kit/angular/v5/components/cometchat-ai-assistant-message-bubble", - "ui-kit/angular/v5/components/cometchat-stream-message-bubble", - "ui-kit/angular/v5/components/cometchat-toolcall-argument-bubble", - "ui-kit/angular/v5/components/cometchat-toolcall-result-bubble", - "ui-kit/angular/v5/components/cometchat-markdown-renderer" + "ui-kit/angular/components/cometchat-smart-replies", + "ui-kit/angular/components/cometchat-conversation-starter", + "ui-kit/angular/components/cometchat-conversation-summary", + "ui-kit/angular/components/cometchat-ai-assistant-chat", + "ui-kit/angular/components/cometchat-ai-assistant-chat-history", + "ui-kit/angular/components/cometchat-ai-assistant-message-bubble", + "ui-kit/angular/components/cometchat-stream-message-bubble", + "ui-kit/angular/components/cometchat-toolcall-argument-bubble", + "ui-kit/angular/components/cometchat-toolcall-result-bubble", + "ui-kit/angular/components/cometchat-markdown-renderer" ] }, { "group": "Base Elements", "pages": [ - "ui-kit/angular/v5/components/avatar", - "ui-kit/angular/v5/components/button", - "ui-kit/angular/v5/components/list-item", - "ui-kit/angular/v5/components/date", - "ui-kit/angular/v5/components/message-preview", - "ui-kit/angular/v5/components/cometchat-search-bar", - "ui-kit/angular/v5/components/cometchat-checkbox", - "ui-kit/angular/v5/components/cometchat-radio-button", - "ui-kit/angular/v5/components/cometchat-dropdown", - "ui-kit/angular/v5/components/cometchat-toast", - "ui-kit/angular/v5/components/action-sheet", - "ui-kit/angular/v5/components/emoji-keyboard", - "ui-kit/angular/v5/components/cometchat-popover", - "ui-kit/angular/v5/components/context-menu", - "ui-kit/angular/v5/components/cometchat-change-scope", - "ui-kit/angular/v5/components/cometchat-media-recorder", - "ui-kit/angular/v5/components/confirm-dialog", - "ui-kit/angular/v5/components/cometchat-fullscreen-viewer", - "ui-kit/angular/v5/components/cometchat-error-boundary", - "ui-kit/angular/v5/components/cometchat-link-dialog", - "ui-kit/angular/v5/components/cometchat-link-popover" + "ui-kit/angular/components/avatar", + "ui-kit/angular/components/button", + "ui-kit/angular/components/list-item", + "ui-kit/angular/components/date", + "ui-kit/angular/components/message-preview", + "ui-kit/angular/components/cometchat-search-bar", + "ui-kit/angular/components/cometchat-checkbox", + "ui-kit/angular/components/cometchat-radio-button", + "ui-kit/angular/components/cometchat-dropdown", + "ui-kit/angular/components/cometchat-toast", + "ui-kit/angular/components/action-sheet", + "ui-kit/angular/components/emoji-keyboard", + "ui-kit/angular/components/cometchat-popover", + "ui-kit/angular/components/context-menu", + "ui-kit/angular/components/cometchat-change-scope", + "ui-kit/angular/components/cometchat-media-recorder", + "ui-kit/angular/components/confirm-dialog", + "ui-kit/angular/components/cometchat-fullscreen-viewer", + "ui-kit/angular/components/cometchat-error-boundary", + "ui-kit/angular/components/cometchat-link-dialog", + "ui-kit/angular/components/cometchat-link-popover" ] } ] @@ -2599,48 +2598,48 @@ { "group": "Reference", "pages": [ - "ui-kit/angular/v5/events", - "ui-kit/angular/v5/methods", - "ui-kit/angular/v5/accessibility", - "ui-kit/angular/v5/troubleshooting" + "ui-kit/angular/events", + "ui-kit/angular/methods", + "ui-kit/angular/accessibility", + "ui-kit/angular/troubleshooting" ] }, { "group": "Guides", "pages": [ - "ui-kit/angular/v5/guides/overview", - "ui-kit/angular/v5/guides/state-management", - "ui-kit/angular/v5/guides/threaded-messages", - "ui-kit/angular/v5/guides/group-chat", - "ui-kit/angular/v5/guides/new-chat", - "ui-kit/angular/v5/guides/block-unblock-user", - "ui-kit/angular/v5/guides/message-privately", - "ui-kit/angular/v5/guides/call-log-details", - "ui-kit/angular/v5/guides/search-messages", - "ui-kit/angular/v5/guides/rich-text-formatting", - "ui-kit/angular/v5/guides/custom-message-types", - "ui-kit/angular/v5/guides/custom-text-formatter", - "ui-kit/angular/v5/guides/mentions-formatter", - "ui-kit/angular/v5/guides/shortcut-formatter", - "ui-kit/angular/v5/guides/hashtag-formatter", - "ui-kit/angular/v5/guides/url-formatter", - "ui-kit/angular/v5/guides/subscription-patterns" + "ui-kit/angular/guides/overview", + "ui-kit/angular/guides/state-management", + "ui-kit/angular/guides/threaded-messages", + "ui-kit/angular/guides/group-chat", + "ui-kit/angular/guides/new-chat", + "ui-kit/angular/guides/block-unblock-user", + "ui-kit/angular/guides/message-privately", + "ui-kit/angular/guides/call-log-details", + "ui-kit/angular/guides/search-messages", + "ui-kit/angular/guides/rich-text-formatting", + "ui-kit/angular/guides/custom-message-types", + "ui-kit/angular/guides/custom-text-formatter", + "ui-kit/angular/guides/mentions-formatter", + "ui-kit/angular/guides/shortcut-formatter", + "ui-kit/angular/guides/hashtag-formatter", + "ui-kit/angular/guides/url-formatter", + "ui-kit/angular/guides/subscription-patterns" ] }, { "group": "Services", "pages": [ - "ui-kit/angular/v5/api-reference/introduction", - "ui-kit/angular/v5/api-reference/chat-state-service", - "ui-kit/angular/v5/api-reference/message-bubble-config-service", - "ui-kit/angular/v5/api-reference/templates-service", - "ui-kit/angular/v5/api-reference/search-conversations-service", - "ui-kit/angular/v5/api-reference/search-messages-service", - "ui-kit/angular/v5/api-reference/formatter-config-service", - "ui-kit/angular/v5/api-reference/rich-text-editor-service" + "ui-kit/angular/api-reference/introduction", + "ui-kit/angular/api-reference/chat-state-service", + "ui-kit/angular/api-reference/message-bubble-config-service", + "ui-kit/angular/api-reference/templates-service", + "ui-kit/angular/api-reference/search-conversations-service", + "ui-kit/angular/api-reference/search-messages-service", + "ui-kit/angular/api-reference/formatter-config-service", + "ui-kit/angular/api-reference/rich-text-editor-service" ] }, - "ui-kit/angular/v5/customization/migration-guide" + "ui-kit/angular/customization/migration-guide" ] } ] @@ -2651,160 +2650,160 @@ { "group": " ", "pages": [ - "ui-kit/angular/overview", - "ui-kit/angular/getting-started", + "ui-kit/angular/v4/overview", + "ui-kit/angular/v4/getting-started", { "group": "Features", "pages": [ { "group": "Chat", "pages": [ - "ui-kit/angular/core-features", - "ui-kit/angular/extensions" + "ui-kit/angular/v4/core-features", + "ui-kit/angular/v4/extensions" ] }, - "ui-kit/angular/call-features", - "ui-kit/angular/ai-features" + "ui-kit/angular/v4/call-features", + "ui-kit/angular/v4/ai-features" ] }, { "group": "Theming", "pages": [ - "ui-kit/angular/theme", - "ui-kit/angular/localize", - "ui-kit/angular/sound-manager" + "ui-kit/angular/v4/theme", + "ui-kit/angular/v4/localize", + "ui-kit/angular/v4/sound-manager" ] }, { "group": "Components", "pages": [ - "ui-kit/angular/components-overview", + "ui-kit/angular/v4/components-overview", { "group": "Conversations", "pages": [ - "ui-kit/angular/conversations", - "ui-kit/angular/conversations-with-messages", - "ui-kit/angular/contacts" + "ui-kit/angular/v4/conversations", + "ui-kit/angular/v4/conversations-with-messages", + "ui-kit/angular/v4/contacts" ] }, { "group": "Users", "pages": [ - "ui-kit/angular/users", - "ui-kit/angular/users-with-messages", - "ui-kit/angular/user-details" + "ui-kit/angular/v4/users", + "ui-kit/angular/v4/users-with-messages", + "ui-kit/angular/v4/user-details" ] }, { "group": "Groups", "pages": [ - "ui-kit/angular/groups", - "ui-kit/angular/groups-with-messages", - "ui-kit/angular/create-group", - "ui-kit/angular/join-protected-group", - "ui-kit/angular/group-members", - "ui-kit/angular/group-add-members", - "ui-kit/angular/group-banned-members", - "ui-kit/angular/group-transfer-ownership", - "ui-kit/angular/group-details" + "ui-kit/angular/v4/groups", + "ui-kit/angular/v4/groups-with-messages", + "ui-kit/angular/v4/create-group", + "ui-kit/angular/v4/join-protected-group", + "ui-kit/angular/v4/group-members", + "ui-kit/angular/v4/group-add-members", + "ui-kit/angular/v4/group-banned-members", + "ui-kit/angular/v4/group-transfer-ownership", + "ui-kit/angular/v4/group-details" ] }, { "group": "Messages", "pages": [ - "ui-kit/angular/messages", - "ui-kit/angular/message-header", - "ui-kit/angular/message-list", - "ui-kit/angular/message-template", - "ui-kit/angular/message-composer", - "ui-kit/angular/message-information", - "ui-kit/angular/threaded-messages" + "ui-kit/angular/v4/messages", + "ui-kit/angular/v4/message-header", + "ui-kit/angular/v4/message-list", + "ui-kit/angular/v4/message-template", + "ui-kit/angular/v4/message-composer", + "ui-kit/angular/v4/message-information", + "ui-kit/angular/v4/threaded-messages" ] }, { "group": "Interactive Messages", "pages": [ - "ui-kit/angular/interactive-form-message", - "ui-kit/angular/interactive-form-bubble", - "ui-kit/angular/interactive-card-message", - "ui-kit/angular/interactive-card-bubble", - "ui-kit/angular/interactive-element-type", - "ui-kit/angular/interactive-action-entity", - "ui-kit/angular/interactive-custom-interactive-message", - "ui-kit/angular/interactive-button-element", - "ui-kit/angular/interactive-text-input-element", - "ui-kit/angular/interactive-label-element", - "ui-kit/angular/interactive-single-select-element", - "ui-kit/angular/interactive-radio-button-element", - "ui-kit/angular/interactive-checkbox-element", - "ui-kit/angular/interactive-dropdown-element", - "ui-kit/angular/interactive-scheduler-message", - "ui-kit/angular/interactive-scheduler-bubble", - "ui-kit/angular/interactive-date-time-picker-element" + "ui-kit/angular/v4/interactive-form-message", + "ui-kit/angular/v4/interactive-form-bubble", + "ui-kit/angular/v4/interactive-card-message", + "ui-kit/angular/v4/interactive-card-bubble", + "ui-kit/angular/v4/interactive-element-type", + "ui-kit/angular/v4/interactive-action-entity", + "ui-kit/angular/v4/interactive-custom-interactive-message", + "ui-kit/angular/v4/interactive-button-element", + "ui-kit/angular/v4/interactive-text-input-element", + "ui-kit/angular/v4/interactive-label-element", + "ui-kit/angular/v4/interactive-single-select-element", + "ui-kit/angular/v4/interactive-radio-button-element", + "ui-kit/angular/v4/interactive-checkbox-element", + "ui-kit/angular/v4/interactive-dropdown-element", + "ui-kit/angular/v4/interactive-scheduler-message", + "ui-kit/angular/v4/interactive-scheduler-bubble", + "ui-kit/angular/v4/interactive-date-time-picker-element" ] }, { "group": "Calls", "pages": [ - "ui-kit/angular/call-overview", - "ui-kit/angular/incoming-call", - "ui-kit/angular/ongoing-call", - "ui-kit/angular/outgoing-call", - "ui-kit/angular/call-buttons", - "ui-kit/angular/call-logs", - "ui-kit/angular/call-log-details", - "ui-kit/angular/call-log-history", - "ui-kit/angular/call-log-recording", - "ui-kit/angular/call-log-participants", - "ui-kit/angular/call-log-with-details" + "ui-kit/angular/v4/call-overview", + "ui-kit/angular/v4/incoming-call", + "ui-kit/angular/v4/ongoing-call", + "ui-kit/angular/v4/outgoing-call", + "ui-kit/angular/v4/call-buttons", + "ui-kit/angular/v4/call-logs", + "ui-kit/angular/v4/call-log-details", + "ui-kit/angular/v4/call-log-history", + "ui-kit/angular/v4/call-log-recording", + "ui-kit/angular/v4/call-log-participants", + "ui-kit/angular/v4/call-log-with-details" ] }, { "group": "Extras", "pages": [ - "ui-kit/angular/reaction", - "ui-kit/angular/reaction-list", - "ui-kit/angular/reaction-info", - "ui-kit/angular/user-member-wrapper" + "ui-kit/angular/v4/reaction", + "ui-kit/angular/v4/reaction-list", + "ui-kit/angular/v4/reaction-info", + "ui-kit/angular/v4/user-member-wrapper" ] }, { "group": "Base Components", "pages": [ - "ui-kit/angular/avatar", - "ui-kit/angular/status-indicator", - "ui-kit/angular/badge", - "ui-kit/angular/receipt", - "ui-kit/angular/date", - "ui-kit/angular/text-bubble", - "ui-kit/angular/image-bubble", - "ui-kit/angular/file-bubble", - "ui-kit/angular/audio-bubble", - "ui-kit/angular/video-bubble", - "ui-kit/angular/message-bubble", - "ui-kit/angular/document-bubble", - "ui-kit/angular/message-input", - "ui-kit/angular/action-sheet", - "ui-kit/angular/media-recorder", - "ui-kit/angular/list-item", - "ui-kit/angular/confirm-dialog", - "ui-kit/angular/backdrop", - "ui-kit/angular/emoji-keyboard", - "ui-kit/angular/modal", - "ui-kit/angular/pop-over", - "ui-kit/angular/icon", - "ui-kit/angular/loader", - "ui-kit/angular/icon-button", - "ui-kit/angular/button-group", - "ui-kit/angular/label", - "ui-kit/angular/input", - "ui-kit/angular/search-input", - "ui-kit/angular/checkbox", - "ui-kit/angular/radio-button", - "ui-kit/angular/dropdown", - "ui-kit/angular/cometchat-quick-view", - "ui-kit/angular/singleselect", - "ui-kit/angular/list" + "ui-kit/angular/v4/avatar", + "ui-kit/angular/v4/status-indicator", + "ui-kit/angular/v4/badge", + "ui-kit/angular/v4/receipt", + "ui-kit/angular/v4/date", + "ui-kit/angular/v4/text-bubble", + "ui-kit/angular/v4/image-bubble", + "ui-kit/angular/v4/file-bubble", + "ui-kit/angular/v4/audio-bubble", + "ui-kit/angular/v4/video-bubble", + "ui-kit/angular/v4/message-bubble", + "ui-kit/angular/v4/document-bubble", + "ui-kit/angular/v4/message-input", + "ui-kit/angular/v4/action-sheet", + "ui-kit/angular/v4/media-recorder", + "ui-kit/angular/v4/list-item", + "ui-kit/angular/v4/confirm-dialog", + "ui-kit/angular/v4/backdrop", + "ui-kit/angular/v4/emoji-keyboard", + "ui-kit/angular/v4/modal", + "ui-kit/angular/v4/pop-over", + "ui-kit/angular/v4/icon", + "ui-kit/angular/v4/loader", + "ui-kit/angular/v4/icon-button", + "ui-kit/angular/v4/button-group", + "ui-kit/angular/v4/label", + "ui-kit/angular/v4/input", + "ui-kit/angular/v4/search-input", + "ui-kit/angular/v4/checkbox", + "ui-kit/angular/v4/radio-button", + "ui-kit/angular/v4/dropdown", + "ui-kit/angular/v4/cometchat-quick-view", + "ui-kit/angular/v4/singleselect", + "ui-kit/angular/v4/list" ] } ] @@ -2812,25 +2811,25 @@ { "group": "Reference", "pages": [ - "ui-kit/angular/methods", - "ui-kit/angular/events" + "ui-kit/angular/v4/methods", + "ui-kit/angular/v4/events" ] }, { "group": "Guides", "pages": [ - "ui-kit/angular/multi-tab-chat-ui-guide", - "ui-kit/angular/new-attachment-option-guide", - "ui-kit/angular/custom-text-formatter-guide", - "ui-kit/angular/mentions-formatter-guide", - "ui-kit/angular/url-formatter-guide", - "ui-kit/angular/shortcut-formatter-guide", - "ui-kit/angular/new-message-option-guide", - "ui-kit/angular/custom-message-guide" + "ui-kit/angular/v4/multi-tab-chat-ui-guide", + "ui-kit/angular/v4/new-attachment-option-guide", + "ui-kit/angular/v4/custom-text-formatter-guide", + "ui-kit/angular/v4/mentions-formatter-guide", + "ui-kit/angular/v4/url-formatter-guide", + "ui-kit/angular/v4/shortcut-formatter-guide", + "ui-kit/angular/v4/new-message-option-guide", + "ui-kit/angular/v4/custom-message-guide" ] }, - "ui-kit/angular/link/sample", - "ui-kit/angular/link/changelog" + "ui-kit/angular/v4/link/sample", + "ui-kit/angular/v4/link/changelog" ] } ] diff --git a/ui-kit/angular/accessibility.mdx b/ui-kit/angular/accessibility.mdx new file mode 100644 index 000000000..2c004c1f5 --- /dev/null +++ b/ui-kit/angular/accessibility.mdx @@ -0,0 +1,80 @@ +--- +title: "Accessibility" +description: "Overview of accessibility support in the CometChat Angular UIKit, including WCAG compliance, keyboard navigation, and ARIA attributes." +--- + +The CometChat Angular UIKit is built with accessibility as a core principle. Components are designed targeting WCAG 2.1 Level AA guidelines, with the goal of making chat interfaces usable by everyone — including users who rely on keyboards, screen readers, or other assistive technologies. + + + Full WCAG compliance requires manual testing with assistive technologies and expert accessibility review. The UIKit implements the patterns and attributes described below, but you should validate your specific integration against your accessibility requirements. + + +Key accessibility features across the UIKit: + +- **WCAG 2.1 Level AA target** — Components are built following the Web Content Accessibility Guidelines for contrast, focus management, and semantic markup. +- **Keyboard navigation** — All interactive components support full keyboard operation using Tab, Enter, Space, Escape, and Arrow keys. Users can navigate, select, and interact without a mouse. +- **ARIA attributes** — Components use appropriate ARIA roles, labels, and states to communicate structure and behavior to screen readers. + +Each component that supports keyboard interaction documents its specific key bindings and focus behavior on its own page. The sections below link to those per-component keyboard accessibility references. + +## Components with Keyboard Accessibility + +### Conversations + +- [CometChatConversations](/ui-kit/angular/components/cometchat-conversations#keyboard-accessibility) +- [CometChatConversationItem](/ui-kit/angular/components/cometchat-conversation-item#keyboard-accessibility) + +### Users + +- [CometChatUsers](/ui-kit/angular/components/cometchat-users#keyboard-accessibility) +- [CometChatUserItem](/ui-kit/angular/components/cometchat-user-item#keyboard-accessibility) + +### Groups + +- [CometChatGroups](/ui-kit/angular/components/cometchat-groups#keyboard-accessibility) +- [CometChatGroupItem](/ui-kit/angular/components/cometchat-group-item#keyboard-accessibility) + +### Group Members + +- [CometChatGroupMembers](/ui-kit/angular/components/cometchat-group-members#keyboard-accessibility) +- [CometChatGroupMemberItem](/ui-kit/angular/components/cometchat-group-member-item#keyboard-accessibility) + +### Messages + +- [CometChatMessageHeader](/ui-kit/angular/components/cometchat-message-header#keyboard-accessibility) +- [CometChatMessageList](/ui-kit/angular/components/cometchat-message-list#keyboard-accessibility) +- [CometChatMessageComposer](/ui-kit/angular/components/cometchat-message-composer#keyboard-accessibility) +- [CometChatMessageBubble](/ui-kit/angular/components/cometchat-message-bubble#keyboard-accessibility) +- [CometChatMessageInformation](/ui-kit/angular/components/cometchat-message-information#keyboard-accessibility) +- [CometChatThreadHeader](/ui-kit/angular/components/cometchat-thread-header#keyboard-accessibility) + +### Message Bubbles + +- [CometChatTextBubble](/ui-kit/angular/components/cometchat-text-bubble#keyboard-accessibility) +- [CometChatImageBubble](/ui-kit/angular/components/cometchat-image-bubble#keyboard-accessibility) +- [CometChatVideoBubble](/ui-kit/angular/components/cometchat-video-bubble#keyboard-accessibility) +- [CometChatAudioBubble](/ui-kit/angular/components/cometchat-audio-bubble#keyboard-accessibility) +- [CometChatFileBubble](/ui-kit/angular/components/cometchat-file-bubble#keyboard-accessibility) +- [CometChatCallBubble](/ui-kit/angular/components/cometchat-call-bubble#keyboard-accessibility) +- [CometChatPollBubble](/ui-kit/angular/components/cometchat-poll-bubble#keyboard-accessibility) +- [CometChatCollaborativeDocumentBubble](/ui-kit/angular/components/cometchat-collaborative-document-bubble#keyboard-accessibility) +- [CometChatCollaborativeWhiteboardBubble](/ui-kit/angular/components/cometchat-collaborative-whiteboard-bubble#keyboard-accessibility) +- [CometChatStickersKeyboard](/ui-kit/angular/components/cometchat-stickers-keyboard#keyboard-accessibility) + +### Reactions + +- [CometChatReactions](/ui-kit/angular/components/cometchat-reactions#keyboard-accessibility) +- [CometChatReactionList](/ui-kit/angular/components/cometchat-reaction-list#keyboard-accessibility) +- [CometChatReactionInfo](/ui-kit/angular/components/cometchat-reaction-info#keyboard-accessibility) + +### Calls + +- [CometChatCallButtons](/ui-kit/angular/components/cometchat-call-buttons#keyboard-accessibility) +- [CometChatIncomingCall](/ui-kit/angular/components/cometchat-incoming-call#keyboard-accessibility) +- [CometChatOutgoingCall](/ui-kit/angular/components/cometchat-outgoing-call#keyboard-accessibility) +- [CometChatCallLogs](/ui-kit/angular/components/cometchat-call-logs#keyboard-accessibility) + +### AI Features + +- [CometChatSmartReplies](/ui-kit/angular/components/cometchat-smart-replies#keyboard-accessibility) +- [CometChatConversationStarter](/ui-kit/angular/components/cometchat-conversation-starter#keyboard-accessibility) diff --git a/ui-kit/angular/ai-features.mdx b/ui-kit/angular/ai-features.mdx index ed1f2838b..fd7f36aa2 100644 --- a/ui-kit/angular/ai-features.mdx +++ b/ui-kit/angular/ai-features.mdx @@ -1,60 +1,157 @@ --- -title: "AI" -description: "AI — CometChat documentation." +title: "AI Smart Chat Features" +description: "AI-powered features in CometChat's Angular UIKit: Conversation Starter, Smart Replies, and Conversation Summary." --- ## Overview -CometChat's AI capabilities greatly enhance user interaction and engagement in your application. Let's understand how the Angular UI Kit achieves these features. +CometChat AI Smart Chat Features enhance user interaction by providing contextual suggestions and summaries. Each feature must be enabled from the CometChat Dashboard first, then activated in your Angular components via input properties. - - - + +AI Smart Chat Features must be enabled from the [CometChat Dashboard](https://app.cometchat.com). Once activated in the dashboard, you enable them in your Angular components using the inputs described below. + -## Conversation Starters +## Smart Chat Features -When a user initiates a new chat, the UI kit displays a list of suggested opening lines that users can select, making it easier for them to start a conversation. These suggestions are powered by CometChat's AI, which predicts contextually relevant conversation starters. +### Conversation Starter -For a comprehensive understanding and guide on implementing and using the Conversation Starters, refer to our specific guide on the [Conversation Starter](/fundamentals/ai-user-copilot/conversation-starter). +Displays AI-generated opening lines when a user starts a new or empty conversation. Helps break the ice by suggesting relevant topics. -Once you have successfully activated the [Conversation Starter](/fundamentals/ai-user-copilot/conversation-starter) from your CometChat Dashboard, the feature will automatically be incorporated into the [MessageList](/ui-kit/angular/message-list) Component of UI Kits. +Enable it on `cometchat-message-list`: - - - +```html + + +``` -## Smart Replies +Or using `ChatStateService` for shared state: -Smart Replies are AI-generated responses to messages. They can predict what a user might want to say next by analyzing the context of the conversation. This allows for quicker and more convenient responses, especially on mobile devices. +```typescript expandable +import { Component } from '@angular/core'; +import { ChatStateService } from '@cometchat/chat-uikit-angular'; +import { CometChatMessageListComponent } from '@cometchat/chat-uikit-angular'; -For a comprehensive understanding and guide on implementing and using the Smart Replies, refer to our specific guide on the [Smart Replies](/fundamentals/ai-user-copilot/smart-replies). +@Component({ + selector: 'app-chat', + standalone: true, + imports: [CometChatMessageListComponent], + template: ` + + + ` +}) +export class ChatComponent { + constructor(private chatState: ChatStateService) {} -Once you have successfully activated the [Smart Replies](/fundamentals/ai-user-copilot/smart-replies) from your CometChat Dashboard, the feature will automatically be incorporated into the Action sheet of [MessageComposer](/ui-kit/angular/message-composer) Component of UI Kits. + openChat(user: CometChat.User): void { + this.chatState.setActiveUser(user); + } +} +``` + +See [CometChatConversationStarter](/ui-kit/angular/components/cometchat-conversation-starter) for the standalone component docs. - + -## Conversation Summary +--- -The Conversation Summary feature provides concise summaries of long conversations, allowing users to catch up quickly on missed chats. This feature uses natural language processing to determine the main points in a conversation. +### Smart Replies -For a comprehensive understanding and guide on implementing and using the Conversation Summary, refer to our specific guide on the [Conversation Summary](/fundamentals/ai-user-copilot/conversation-summary). +AI-generated response suggestions based on the last received message. Users can tap a suggestion to send it instantly. -Once you have successfully activated the [Smart Replies](/fundamentals/ai-user-copilot/smart-replies) from your CometChat Dashboard, the feature will automatically be incorporated into the Action sheet of [MessageComposer](/ui-kit/angular/message-composer) Component of UI Kits. +Enable it on `cometchat-message-list`: - - - +```html + + +``` -## AI Assist Bot +Smart replies appear above the message composer when a qualifying message is received. The component respects configurable trigger keywords and a delay before showing suggestions. -AI Assist Bots automate responses and prompt interaction in the chat. They can handle common queries, assist with tasks, and provide information, freeing up human resources for more complex issues. +See [CometChatSmartReplies](/ui-kit/angular/components/cometchat-smart-replies) for the standalone component docs. -For a comprehensive understanding and guide on implementing and using the AI Assist Bot, refer to our specific guide on the [AI Assist Bot](http://localhost:3000/docs-beta/ai/bots). + + + + +--- -Once you have successfully activated the [AI Assist Bot](http://localhost:3000/docs-beta/ai/bots) from your CometChat Dashboard, the feature will automatically be incorporated into the Action sheet of [MessageComposer](/ui-kit/angular/message-composer) Component of UI Kits. +### Conversation Summary + +AI-generated recap of long conversations. Useful for catching up on missed messages without scrolling through the entire thread. + +Enable it on `cometchat-message-header`: + +```html + + + +``` + +Full example combining the header with conversation starters and smart replies in the message list: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatMessageListComponent, + CometChatMessageHeaderComponent, + ChatStateService +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-chat-view', + standalone: true, + imports: [CometChatMessageListComponent, CometChatMessageHeaderComponent], + template: ` + + + + + + ` +}) +export class ChatViewComponent { + constructor(private chatState: ChatStateService) {} + + openChat(user: CometChat.User): void { + this.chatState.setActiveUser(user); + } +} +``` + +See [CometChatConversationSummary](/ui-kit/angular/components/cometchat-conversation-summary) for the standalone component docs. - + + +--- + +## Next Steps + + + + Customize the message list where AI Smart Chat Features appear + + + Enable Conversation Summary in the header + + + Standalone Conversation Starter component + + + Standalone Smart Replies component + + diff --git a/ui-kit/angular/v5/angular-conversation.mdx b/ui-kit/angular/angular-conversation.mdx similarity index 95% rename from ui-kit/angular/v5/angular-conversation.mdx rename to ui-kit/angular/angular-conversation.mdx index 3a03d26a4..c555370be 100644 --- a/ui-kit/angular/v5/angular-conversation.mdx +++ b/ui-kit/angular/angular-conversation.mdx @@ -12,14 +12,14 @@ description: "Build a two-panel conversation list + message view layout in Angul | Framework | Angular | | Components | `CometChatConversationsComponent`, `CometChatMessageHeaderComponent`, `CometChatMessageListComponent`, `CometChatMessageComposerComponent` | | Layout | Two-panel — conversation list (left) + message view (right) | -| Prerequisite | Complete [Angular Integration](/ui-kit/angular/v5/integration) Steps 1–5 first | +| Prerequisite | Complete [Angular Integration](/ui-kit/angular/integration) Steps 1–5 first | | Pattern | WhatsApp Web, Slack, Microsoft Teams | This guide builds a two-panel chat layout — conversation list on the left, messages on the right. Users tap a conversation to open it. -This assumes you've already completed [Angular Integration](/ui-kit/angular/v5/integration) (project created, UI Kit installed, init + login working, CSS imported). +This assumes you've already completed [Angular Integration](/ui-kit/angular/integration) (project created, UI Kit installed, init + login working, CSS imported). @@ -143,7 +143,7 @@ How it works: This is the recommended approach for most applications. For advanced use cases like multi-panel layouts where each panel needs independent state, you can pass `[user]` or `[group]` via `@Input()` bindings - to override the service. See the [State Management guide](/ui-kit/angular/v5/guides/state-management) for details. + to override the service. See the [State Management guide](/ui-kit/angular/guides/state-management) for details. --- @@ -161,16 +161,16 @@ You should see the conversation list on the left. Tap any conversation to load m ## Next Steps - + Customize colors, fonts, and styles to match your brand - + Browse all prebuilt UI components - + Back to the main setup guide - + Chat features included out of the box diff --git a/ui-kit/angular/v5/angular-one-to-one-chat.mdx b/ui-kit/angular/angular-one-to-one-chat.mdx similarity index 96% rename from ui-kit/angular/v5/angular-one-to-one-chat.mdx rename to ui-kit/angular/angular-one-to-one-chat.mdx index e3e1c225c..561b5c960 100644 --- a/ui-kit/angular/v5/angular-one-to-one-chat.mdx +++ b/ui-kit/angular/angular-one-to-one-chat.mdx @@ -12,14 +12,14 @@ description: "Build a focused one-to-one or group chat experience in Angular wit | Framework | Angular | | Components | `CometChatMessageHeaderComponent`, `CometChatMessageListComponent`, `CometChatMessageComposerComponent` | | Layout | Single chat window — no sidebar, no conversation list | -| Prerequisite | Complete [Angular Integration](/ui-kit/angular/v5/integration) Steps 1–5 first | +| Prerequisite | Complete [Angular Integration](/ui-kit/angular/integration) Steps 1–5 first | | Pattern | Support chat, embedded widgets, focused messaging | This guide builds a single chat window — no sidebar, no conversation list. Users go directly into a one-to-one or group chat. Good for support chat, embedded widgets, or any focused messaging experience. -This assumes you've already completed [Angular Integration](/ui-kit/angular/v5/integration) (project created, UI Kit installed, init + login working, CSS imported). +This assumes you've already completed [Angular Integration](/ui-kit/angular/integration) (project created, UI Kit installed, init + login working, CSS imported). @@ -180,16 +180,16 @@ You should see the chat window load with the conversation for the UID you set. ## Next Steps - + Customize colors, fonts, and styles to match your brand - + Browse all prebuilt UI components - + Back to the main setup guide - + Chat features included out of the box diff --git a/ui-kit/angular/v5/angular-tab-based-chat.mdx b/ui-kit/angular/angular-tab-based-chat.mdx similarity index 96% rename from ui-kit/angular/v5/angular-tab-based-chat.mdx rename to ui-kit/angular/angular-tab-based-chat.mdx index 9ce3db465..85ff2aa5a 100644 --- a/ui-kit/angular/v5/angular-tab-based-chat.mdx +++ b/ui-kit/angular/angular-tab-based-chat.mdx @@ -12,14 +12,14 @@ description: "Build a tab-based messaging UI with chats, calls, users, and group | Framework | Angular | | Components | `CometChatConversationsComponent`, `CometChatCallLogsComponent`, `CometChatUsersComponent`, `CometChatMessageHeaderComponent`, `CometChatMessageListComponent`, `CometChatMessageComposerComponent` | | Layout | Tabbed sidebar (Chats, Calls, Users) + message view | -| Prerequisite | Complete [Angular Integration](/ui-kit/angular/v5/integration) Steps 1–5 first | +| Prerequisite | Complete [Angular Integration](/ui-kit/angular/integration) Steps 1–5 first | | Pattern | Full-featured messaging app with multiple sections | This guide builds a tabbed messaging UI — Chats, Calls, and Users tabs, with a message view. Good for full-featured apps that need more than just conversations. -This assumes you've already completed [Angular Integration](/ui-kit/angular/v5/integration) (project created, UI Kit installed, init + login working, CSS imported). +This assumes you've already completed [Angular Integration](/ui-kit/angular/integration) (project created, UI Kit installed, init + login working, CSS imported). @@ -218,16 +218,16 @@ You should see the tab bar at the top of the sidebar. Switch between Chats, Call ## Next Steps - + Customize colors, fonts, and styles to match your brand - + Browse all prebuilt UI components - + Back to the main setup guide - + Chat features included out of the box diff --git a/ui-kit/angular/v5/api-reference/chat-state-service.mdx b/ui-kit/angular/api-reference/chat-state-service.mdx similarity index 87% rename from ui-kit/angular/v5/api-reference/chat-state-service.mdx rename to ui-kit/angular/api-reference/chat-state-service.mdx index 97e219b0a..36681ba60 100644 --- a/ui-kit/angular/v5/api-reference/chat-state-service.mdx +++ b/ui-kit/angular/api-reference/chat-state-service.mdx @@ -300,18 +300,18 @@ The following Chat-Aware Components integrate with `ChatStateService`. Each supp | Component | Key Inputs | Service Integration | |-----------|-----------|---------------------| -| [CometChatConversations](/ui-kit/angular/v5/components/cometchat-conversations) | — | Calls `setActiveConversation()` when a conversation is selected | -| [CometChatMessageHeader](/ui-kit/angular/v5/components/cometchat-message-header) | `[user]`, `[group]` | Subscribes to `activeUser` / `activeGroup` | -| [CometChatMessageList](/ui-kit/angular/v5/components/cometchat-message-list) | `[user]`, `[group]` | Subscribes to `activeUser` / `activeGroup` | -| [CometChatMessageComposer](/ui-kit/angular/v5/components/cometchat-message-composer) | `[user]`, `[group]` | Subscribes to `activeUser` / `activeGroup` | -| [CometChatUsers](/ui-kit/angular/v5/components/cometchat-users) | — | Calls `setActiveUser()` when a user is selected | -| [CometChatGroups](/ui-kit/angular/v5/components/cometchat-groups) | — | Calls `setActiveGroup()` when a group is selected | -| [CometChatGroupMembers](/ui-kit/angular/v5/components/cometchat-group-members) | `[group]` | Subscribes to `activeGroup` | -| [CometChatThreadHeader](/ui-kit/angular/v5/components/cometchat-thread-header) | `[parentMessage]` | Receives parent message context from ChatStateService | +| [CometChatConversations](/ui-kit/angular/components/cometchat-conversations) | — | Calls `setActiveConversation()` when a conversation is selected | +| [CometChatMessageHeader](/ui-kit/angular/components/cometchat-message-header) | `[user]`, `[group]` | Subscribes to `activeUser` / `activeGroup` | +| [CometChatMessageList](/ui-kit/angular/components/cometchat-message-list) | `[user]`, `[group]` | Subscribes to `activeUser` / `activeGroup` | +| [CometChatMessageComposer](/ui-kit/angular/components/cometchat-message-composer) | `[user]`, `[group]` | Subscribes to `activeUser` / `activeGroup` | +| [CometChatUsers](/ui-kit/angular/components/cometchat-users) | — | Calls `setActiveUser()` when a user is selected | +| [CometChatGroups](/ui-kit/angular/components/cometchat-groups) | — | Calls `setActiveGroup()` when a group is selected | +| [CometChatGroupMembers](/ui-kit/angular/components/cometchat-group-members) | `[group]` | Subscribes to `activeGroup` | +| [CometChatThreadHeader](/ui-kit/angular/components/cometchat-thread-header) | `[parentMessage]` | Receives parent message context from ChatStateService | ## See Also -- [State Management Guide](/ui-kit/angular/v5/guides/state-management) — Hybrid Approach architecture and decision matrix -- [Service Reference](/ui-kit/angular/v5/api-reference/introduction) — Overview of all documented services -- [FormatterConfigService](/ui-kit/angular/v5/api-reference/formatter-config-service) — Text formatter configuration service -- [RichTextEditorService](/ui-kit/angular/v5/api-reference/rich-text-editor-service) — Rich text editor configuration service +- [State Management Guide](/ui-kit/angular/guides/state-management) — Hybrid Approach architecture and decision matrix +- [Service Reference](/ui-kit/angular/api-reference/introduction) — Overview of all documented services +- [FormatterConfigService](/ui-kit/angular/api-reference/formatter-config-service) — Text formatter configuration service +- [RichTextEditorService](/ui-kit/angular/api-reference/rich-text-editor-service) — Rich text editor configuration service diff --git a/ui-kit/angular/v5/api-reference/formatter-config-service.mdx b/ui-kit/angular/api-reference/formatter-config-service.mdx similarity index 98% rename from ui-kit/angular/v5/api-reference/formatter-config-service.mdx rename to ui-kit/angular/api-reference/formatter-config-service.mdx index 89582e4cf..a2cdc3eb6 100644 --- a/ui-kit/angular/v5/api-reference/formatter-config-service.mdx +++ b/ui-kit/angular/api-reference/formatter-config-service.mdx @@ -809,11 +809,11 @@ export class ThreadPanelComponent implements OnInit { Angular's hierarchical DI ensures the message list inside the wrapper resolves the local `FormatterConfigService` instance. The main chat panel continues to use the root singleton. -See [CometChatMessageList — Multiple Message Lists with Different Configurations](/ui-kit/angular/v5/components/cometchat-message-list#multiple-message-lists-with-different-configurations) for a complete multi-panel example. +See [CometChatMessageList — Multiple Message Lists with Different Configurations](/ui-kit/angular/components/cometchat-message-list#multiple-message-lists-with-different-configurations) for a complete multi-panel example. ## See Also - [CometChatTextBubble Component](../components/cometchat-text-bubble.mdx) - [CometChatMessageList Component](../components/cometchat-message-list.mdx) - [CometChatConversations Component](../components/cometchat-conversations.mdx) -- [Rich Text Formatting Guide](/ui-kit/angular/v5/guides/custom-text-formatter) +- [Rich Text Formatting Guide](/ui-kit/angular/guides/custom-text-formatter) diff --git a/ui-kit/angular/v5/api-reference/introduction.mdx b/ui-kit/angular/api-reference/introduction.mdx similarity index 88% rename from ui-kit/angular/v5/api-reference/introduction.mdx rename to ui-kit/angular/api-reference/introduction.mdx index 0614d66fe..4c1b21f41 100644 --- a/ui-kit/angular/v5/api-reference/introduction.mdx +++ b/ui-kit/angular/api-reference/introduction.mdx @@ -76,12 +76,12 @@ const uiKitSettings = new UIKitSettingsBuilder() | Service | Description | Reference | |---------|-------------|-----------| -| ChatStateService | Centralized chat state management service — tracks active user, group, and conversation across components | [ChatStateService](/ui-kit/angular/v5/api-reference/chat-state-service) | -| MessageBubbleConfigService | Centralized message bubble view configuration — customize bubble parts globally or per message type | [MessageBubbleConfigService](/ui-kit/angular/v5/api-reference/message-bubble-config-service) | -| CometChatTemplatesService | SDK-wide template customization service — shared and component-specific templates for all list components | [CometChatTemplatesService](/ui-kit/angular/v5/api-reference/templates-service) | -| FormatterConfigService | Text formatter configuration service — manages text formatting rules and custom formatters | [FormatterConfigService](/ui-kit/angular/v5/api-reference/formatter-config-service) | -| RichTextEditorService | Rich text editor configuration service — controls editor behavior, toolbar options, and input modes | [RichTextEditorService](/ui-kit/angular/v5/api-reference/rich-text-editor-service) | -| GlobalConfig | Centralized configuration via `COMETCHAT_GLOBAL_CONFIG` injection token — sets defaults for display, sound, calls, and more across all components | [GlobalConfig](/ui-kit/angular/v5/customization/global-config) | +| ChatStateService | Centralized chat state management service — tracks active user, group, and conversation across components | [ChatStateService](/ui-kit/angular/api-reference/chat-state-service) | +| MessageBubbleConfigService | Centralized message bubble view configuration — customize bubble parts globally or per message type | [MessageBubbleConfigService](/ui-kit/angular/api-reference/message-bubble-config-service) | +| CometChatTemplatesService | SDK-wide template customization service — shared and component-specific templates for all list components | [CometChatTemplatesService](/ui-kit/angular/api-reference/templates-service) | +| FormatterConfigService | Text formatter configuration service — manages text formatting rules and custom formatters | [FormatterConfigService](/ui-kit/angular/api-reference/formatter-config-service) | +| RichTextEditorService | Rich text editor configuration service — controls editor behavior, toolbar options, and input modes | [RichTextEditorService](/ui-kit/angular/api-reference/rich-text-editor-service) | +| GlobalConfig | Centralized configuration via `COMETCHAT_GLOBAL_CONFIG` injection token — sets defaults for display, sound, calls, and more across all components | [GlobalConfig](/ui-kit/angular/customization/global-config) | ### Service Scoping (Multiple Instances) @@ -98,7 +98,7 @@ If you need different configurations for different component instances (e.g., a export class ThreadPanelComponent { } ``` -See the [State Management Guide — Scoping Customization Services](/ui-kit/angular/v5/guides/state-management#scoping-customization-services-for-multiple-instances) and [CometChatMessageList — Multiple Message Lists](/ui-kit/angular/v5/components/cometchat-message-list#multiple-message-lists-with-different-configurations) for complete examples. +See the [State Management Guide — Scoping Customization Services](/ui-kit/angular/guides/state-management#scoping-customization-services-for-multiple-instances) and [CometChatMessageList — Multiple Message Lists](/ui-kit/angular/components/cometchat-message-list#multiple-message-lists-with-different-configurations) for complete examples. Do not scope `ChatStateService` — it is intentionally a singleton that tracks the app-wide active conversation. Use the props-based pattern (`[user]`, `[group]`) for independent panels instead. @@ -132,7 +132,7 @@ CometChatUserEvents.ccUserUnblocked.subscribe((user) => { }); ``` -See the full [Events reference](/ui-kit/angular/v5/events) for all available event names. +See the full [Events reference](/ui-kit/angular/events) for all available event names. ## Type Definitions diff --git a/ui-kit/angular/v5/api-reference/message-bubble-config-service.mdx b/ui-kit/angular/api-reference/message-bubble-config-service.mdx similarity index 93% rename from ui-kit/angular/v5/api-reference/message-bubble-config-service.mdx rename to ui-kit/angular/api-reference/message-bubble-config-service.mdx index be7fe2204..79111b0b7 100644 --- a/ui-kit/angular/v5/api-reference/message-bubble-config-service.mdx +++ b/ui-kit/angular/api-reference/message-bubble-config-service.mdx @@ -303,8 +303,8 @@ The main chat panel continues to use the root singleton, unaffected by the threa ## See Also -- [CometChatMessageList](/ui-kit/angular/v5/components/cometchat-message-list) — The primary consumer of this service -- [CometChatMessageBubble](/ui-kit/angular/v5/components/cometchat-message-bubble) — Individual bubble component -- [Custom Message Types](/ui-kit/angular/v5/components/cometchat-message-bubble) — Register templates for custom message types -- [State Management Guide — Scoping](/ui-kit/angular/v5/guides/state-management#scoping-customization-services-for-multiple-instances) — DI scoping patterns -- [API Reference Introduction](/ui-kit/angular/v5/api-reference/introduction) — Overview of all services +- [CometChatMessageList](/ui-kit/angular/components/cometchat-message-list) — The primary consumer of this service +- [CometChatMessageBubble](/ui-kit/angular/components/cometchat-message-bubble) — Individual bubble component +- [Custom Message Types](/ui-kit/angular/components/cometchat-message-bubble) — Register templates for custom message types +- [State Management Guide — Scoping](/ui-kit/angular/guides/state-management#scoping-customization-services-for-multiple-instances) — DI scoping patterns +- [API Reference Introduction](/ui-kit/angular/api-reference/introduction) — Overview of all services diff --git a/ui-kit/angular/v5/api-reference/rich-text-editor-service.mdx b/ui-kit/angular/api-reference/rich-text-editor-service.mdx similarity index 97% rename from ui-kit/angular/v5/api-reference/rich-text-editor-service.mdx rename to ui-kit/angular/api-reference/rich-text-editor-service.mdx index c803ca969..ebcb49dc5 100644 --- a/ui-kit/angular/v5/api-reference/rich-text-editor-service.mdx +++ b/ui-kit/angular/api-reference/rich-text-editor-service.mdx @@ -532,9 +532,9 @@ export class ThreadComposerComponent { } ``` -See [CometChatMessageList — Multiple Message Lists with Different Configurations](/ui-kit/angular/v5/components/cometchat-message-list#multiple-message-lists-with-different-configurations) for a complete multi-panel example. +See [CometChatMessageList — Multiple Message Lists with Different Configurations](/ui-kit/angular/components/cometchat-message-list#multiple-message-lists-with-different-configurations) for a complete multi-panel example. ## See Also -- [MessageComposer Component](/ui-kit/angular/v5/components/cometchat-message-composer) -- [Rich Text Formatting Guide](/ui-kit/angular/v5/guides/custom-text-formatter) +- [MessageComposer Component](/ui-kit/angular/components/cometchat-message-composer) +- [Rich Text Formatting Guide](/ui-kit/angular/guides/custom-text-formatter) diff --git a/ui-kit/angular/v5/api-reference/search-conversations-service.mdx b/ui-kit/angular/api-reference/search-conversations-service.mdx similarity index 100% rename from ui-kit/angular/v5/api-reference/search-conversations-service.mdx rename to ui-kit/angular/api-reference/search-conversations-service.mdx diff --git a/ui-kit/angular/v5/api-reference/search-messages-service.mdx b/ui-kit/angular/api-reference/search-messages-service.mdx similarity index 100% rename from ui-kit/angular/v5/api-reference/search-messages-service.mdx rename to ui-kit/angular/api-reference/search-messages-service.mdx diff --git a/ui-kit/angular/v5/api-reference/templates-service.mdx b/ui-kit/angular/api-reference/templates-service.mdx similarity index 100% rename from ui-kit/angular/v5/api-reference/templates-service.mdx rename to ui-kit/angular/api-reference/templates-service.mdx diff --git a/ui-kit/angular/call-features.mdx b/ui-kit/angular/call-features.mdx index 4c2cfe4e5..07c2b4f96 100644 --- a/ui-kit/angular/call-features.mdx +++ b/ui-kit/angular/call-features.mdx @@ -1,66 +1,109 @@ --- -title: "Call" -description: "Call — CometChat documentation." +title: "Call Features" +description: "Overview of CometChat's audio and video calling features including incoming calls, outgoing calls, and call logs — with the Angular UIKit components that power each feature." --- ## Overview -CometChat's Calls feature is an advanced functionality that allows you to seamlessly integrate one-on-one as well as group audio and video calling capabilities into your application. This document provides a technical overview of these features, as implemented in the Angular UI Kit. +CometChat Calls integrates 1:1 and group audio/video calling into the Angular UIKit. Install the Calls SDK and the UIKit auto-detects it, enabling call components. + + +Ensure `CometChatUIKit.init()` has completed and the user is logged in before using call features. + ## Integration -First, make sure that you've correctly integrated the UI Kit library into your project. If you haven't done this yet or are facing difficulties, refer to our [Getting Started](/ui-kit/angular/getting-started) guide. This guide will walk you through a step-by-step process of integrating our UI Kit into your Angular project. +Install the Calls SDK: -Once you've successfully integrated the UI Kit, the next step is to add the CometChat Calls SDK to your project. This is necessary to enable the calling features in the UI Kit. Here's how you do it: +```bash +npm install @cometchat/calls-sdk-javascript +``` -Add the SDK files to your project's dependencies or libraries: +Then enable calling in your `UIKitSettingsBuilder`: -```java -npm install @cometchat/calls-sdk-javascript +```typescript +const settings = new UIKitSettingsBuilder() + .setAppId('APP_ID') + .setRegion('REGION') + .setAuthKey('AUTH_KEY') + .setCallingEnabled(true) + .build(); ``` -After adding this dependency, the Angular UI Kit will automatically detect it and activate the calling features. Now, your application supports both audio and video calling. You will see [CallButtons](/ui-kit/angular/call-buttons) component rendered in [MessageHeader](/ui-kit/angular/message-header) Component. + + Both steps are required. Installing the Calls SDK alone is not enough — you must also call `.setCallingEnabled(true)` on the builder. Without it, call buttons remain hidden and the Calls SDK is not initialized. + - - - +When enabled, [cometchat-call-buttons](/ui-kit/angular/components/cometchat-call-buttons) renders in [cometchat-message-header](/ui-kit/angular/components/cometchat-message-header) and the trailing call button appears in [cometchat-call-logs](/ui-kit/angular/components/cometchat-call-logs). -## Features +### Custom Call App Settings -### Incoming Call +By default the UIKit builds `CallAppSettings` from your `appId` and `region`. If you need custom configuration (e.g. a different region for the Calls SDK or additional settings), pass your own `CallAppSettings`: -The [Incoming Call](/ui-kit/angular/incoming-call) component of the CometChat UI Kit provides the functionality that lets users receive real-time audio and video calls in the app. +```typescript +import { CometChatUIKitCalls } from '@cometchat/calls-sdk-javascript'; -When a call is made to a user, the Incoming Call component triggers and displays a call screen. This call screen typically displays the caller information and provides the user with options to either accept or reject the incoming call. +const callAppSettings = new CometChatUIKitCalls.CallAppSettingsBuilder() + .setAppId('APP_ID') + .setRegion('REGION') + .build(); + +const settings = new UIKitSettingsBuilder() + .setAppId('APP_ID') + .setRegion('REGION') + .setAuthKey('AUTH_KEY') + .setCallingEnabled(true) + .setCallAppSettings(callAppSettings) + .build(); +``` + + + `setCallAppSettings` is optional. When omitted, the UIKit automatically creates settings from the `appId` and `region` you already provided. + - + -### Outgoing Call +## Features -The [Outgoing Call](/ui-kit/angular/outgoing-call) component of the CometChat UI Kit is designed to manage the outgoing call process within your application. When a user initiates an audio or video call to another user or group, this component displays an outgoing call screen, showcasing information about the recipient and the call status. +### Incoming Call -Importantly, the Outgoing Call component is smartly designed to transition automatically into the ongoing call screen once the receiver accepts the call. This ensures a smooth flow from initiating the call to engaging in a conversation, without any additional steps required from the user. +The [cometchat-incoming-call](/ui-kit/angular/components/cometchat-incoming-call) component displays a call screen when a call is received, showing caller info with accept/reject options. - + -### Ongoing Call - -The [OngoingCall](/ui-kit/angular/ongoing-call) component is the user interface that displays during an ongoing call. For an audio call, this screen displays the participant's name, call duration, and buttons to mute the audio, enable or disable video, switch the camera, and end the call. +### Outgoing Call -For a video call, the Call Screen might additionally display the video footage from both the user's camera and the recipient's camera. +The [cometchat-outgoing-call](/ui-kit/angular/components/cometchat-outgoing-call) component displays an outgoing call screen with recipient info and call status. Transitions to the ongoing call screen when the receiver accepts. - + ### Call Logs -[Call Logs](/ui-kit/angular/call-logs) component provides you with the records call events such as who called who, the time of the call, and the duration of the call. This information can be fetched from the CometChat server and displayed in a structured format for users to view their past call activities. +The [cometchat-call-logs](/ui-kit/angular/components/cometchat-call-logs) component displays call history — caller, time, and duration. - + + +## Next Steps + + + + Audio and video call buttons + + + Incoming call notifications and UI + + + Call history and details + + + Core chat features + + diff --git a/ui-kit/angular/components/action-sheet.mdx b/ui-kit/angular/components/action-sheet.mdx new file mode 100644 index 000000000..c33d06370 --- /dev/null +++ b/ui-kit/angular/components/action-sheet.mdx @@ -0,0 +1,693 @@ +--- +title: Action Sheet +description: "Display a list of actions in a bottom sheet or popover menu" +--- +The `CometChatActionSheet` component displays a list of action items in a sheet-style menu. It provides a clean, accessible interface for presenting multiple options to users, commonly used for message actions, attachment options, and contextual menus. + +## Overview + +The ActionSheet component provides a flexible way to display action menus: + +- **Flexible Actions**: Supports various action types with icons and titles +- **Keyboard Accessible**: Full keyboard navigation with arrow keys, Enter, and Escape +- **ARIA Compliant**: Proper roles and attributes for screen readers +- **Focus Management**: Automatic focus trapping and restoration +- **Customizable**: CSS variable-based styling for easy theming + +## Basic Usage + +### Simple Action Sheet + +```html + + +``` + +### Complete Example + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatActionSheetComponent } from '@cometchat/chat-uikit-angular'; +import { CometChatMessageComposerAction } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-message-actions', + standalone: true, + imports: [CometChatActionSheetComponent], + template: ` + + + ` +}) +export class MessageActionsComponent { + messageActions: CometChatMessageComposerAction[] = [ + new CometChatMessageComposerAction({ + id: 'reply', + title: 'Reply', + iconURL: 'assets/icons/reply.svg', + onClick: () => this.replyToMessage() + }), + new CometChatMessageComposerAction({ + id: 'forward', + title: 'Forward', + iconURL: 'assets/icons/forward.svg', + onClick: () => this.forwardMessage() + }), + new CometChatMessageComposerAction({ + id: 'delete', + title: 'Delete', + iconURL: 'assets/icons/delete.svg', + onClick: () => this.deleteMessage() + }) + ]; + + handleAction(action: CometChatMessageComposerAction): void { + console.log('Action selected:', action.title); + if (action.onClick) { + action.onClick(); + } + } + + onClose(): void { + console.log('Action sheet closed'); + } + + replyToMessage(): void { + console.log('Replying to message'); + } + + forwardMessage(): void { + console.log('Forwarding message'); + } + + deleteMessage(): void { + console.log('Deleting message'); + } +} +``` + + + **Live Preview** — interact with the default action sheet. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-action-sheet--default) + + + + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `actions` | `(CometChatMessageComposerAction \| CometChatActionsView)[]` | `[]` | Array of action items to display in the sheet. Each action must have an `id`, `title`, and `iconURL` | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `actionItemClick` | `EventEmitter` | Emitted when an action item is clicked. Provides the clicked action object | +| `closeSheet` | `EventEmitter` | Emitted when the sheet should be closed (e.g., Escape key pressed) | + +## Action Types + +### CometChatMessageComposerAction + +The primary action type used in the ActionSheet: + +```typescript +class CometChatMessageComposerAction { + id: string; // Unique identifier + title: string; // Display text + iconURL: string; // Icon image URL + onClick?: () => void; // Optional click handler +} +``` + +### Creating Actions + +```typescript expandable +import { CometChatMessageComposerAction } from '@cometchat/chat-uikit-angular'; + +const editAction = new CometChatMessageComposerAction({ + id: 'edit', + title: 'Edit Message', + iconURL: 'assets/icons/edit.svg', + onClick: () => { + console.log('Edit clicked'); + } +}); +``` + +## Behavior + +### Action Selection + +When a user clicks or selects an action: +1. The `actionItemClick` event is emitted with the action object +2. If the action has an `onClick` handler, it can be invoked +3. The parent component typically closes the sheet after handling the action + +### Keyboard Navigation + +The ActionSheet provides comprehensive keyboard support: + +| Key | Action | +|-----|--------| +| `Tab` / `Shift+Tab` | Navigate between actions (focus trapped within sheet) | +| `Arrow Down` | Move focus to next action | +| `Arrow Up` | Move focus to previous action | +| `Enter` / `Space` | Select the focused action | +| `Escape` | Close the action sheet | + +Focus automatically moves to the first action when the sheet opens. + +## Customization + +### Styling with CSS Variables + +The ActionSheet component uses CSS variables for easy customization: + +```css expandable +/* Custom action sheet styling */ +cometchat-action-sheet { + --cometchat-radius-4: 12px; + --cometchat-border-color-light: #E0E0E0; + --cometchat-background-color-01: #FFFFFF; + --cometchat-white-hover: #F5F5F5; + --cometchat-primary-color: #4A90E2; + --cometchat-icon-color-highlight: #4A90E2; + --cometchat-text-color-primary: #000000; + --cometchat-font-heading4-regular: 400 16px 'Inter'; + --cometchat-padding-2: 8px; + --cometchat-padding-4: 16px; +} +``` + +### Available CSS Variables + +| Variable | Purpose | Default | +|----------|---------|---------| +| `--cometchat-radius-4` | Border radius of sheet | `16px` | +| `--cometchat-border-color-light` | Border color | `#F5F5F5` | +| `--cometchat-background-color-01` | Sheet background | `#FFF` | +| `--cometchat-white-hover` | Hover/focus background | `rgba(0,0,0,0.04)` | +| `--cometchat-primary-color` | Focus outline color | `#6852D6` | +| `--cometchat-icon-color-highlight` | Icon color | `#6852D6` | +| `--cometchat-text-color-primary` | Text color | `#141414` | +| `--cometchat-font-heading4-regular` | Action text font | `400 16px Roboto` | +| `--cometchat-padding-2` | Icon-text gap | `8px` | +| `--cometchat-padding-4` | Action item padding | `16px` | + +### Custom Dimensions + +```css expandable +/* Wider action sheet */ +.wide-sheet cometchat-action-sheet .cometchat-action-sheet { + width: 400px; +} + +/* Taller action sheet */ +.tall-sheet cometchat-action-sheet .cometchat-action-sheet { + height: 500px; +} + +/* Compact action sheet */ +.compact-sheet cometchat-action-sheet .cometchat-action-sheet__item { + padding: 12px; +} +``` + +### Custom Colors + +```css expandable +/* Dark theme action sheet */ +.dark-theme cometchat-action-sheet { + --cometchat-background-color-01: #1E1E1E; + --cometchat-text-color-primary: #FFFFFF; + --cometchat-border-color-light: #333333; + --cometchat-white-hover: rgba(255, 255, 255, 0.1); + --cometchat-icon-color-highlight: #BB86FC; + --cometchat-primary-color: #BB86FC; +} + +/* Custom brand colors */ +.branded-sheet cometchat-action-sheet { + --cometchat-icon-color-highlight: #FF6B35; + --cometchat-primary-color: #FF6B35; +} +``` + + + **Live Preview** — interactive action sheet with live controls. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-action-sheet--default-actions-list) + + + + +## Accessibility + +The ActionSheet component includes comprehensive accessibility features: + +### ARIA Attributes + +- **Dialog Role**: `role="dialog"` and `aria-modal="true"` indicate modal behavior +- **Menu Semantics**: `role="menu"` for the action list, `role="menuitem"` for each action +- **Labels**: `aria-label` on the dialog and each action for screen reader context +- **Focus Management**: Automatic focus on first item, focus trapping within sheet + +### Screen Reader Support + +Screen readers will announce: +- "Action sheet, dialog" when the sheet opens +- Each action's title as users navigate +- "Selected" when an action is activated + +### Focus Management + +- **Initial Focus**: Automatically set to the first action item +- **Focus Trapping**: Tab key cycles through actions without leaving the sheet +- **Focus Restoration**: When closed, focus returns to the triggering element (handled by parent) + +### Visual Focus Indicators + +Clear focus indicators for keyboard navigation: +- 2px solid outline in primary color +- Inset outline (-2px offset) to stay within bounds +- Background highlight on focus + +## Examples + +### Message Context Menu + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatActionSheetComponent } from '@cometchat/chat-uikit-angular'; +import { CometChatMessageComposerAction } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-message-context-menu', + standalone: true, + imports: [CometChatActionSheetComponent], + template: ` + @if (showActions) { + + + } + ` +}) +export class MessageContextMenuComponent { + showActions = false; + + contextActions: CometChatMessageComposerAction[] = [ + new CometChatMessageComposerAction({ + id: 'reply', + title: 'Reply', + iconURL: 'assets/icons/reply.svg' + }), + new CometChatMessageComposerAction({ + id: 'forward', + title: 'Forward', + iconURL: 'assets/icons/forward.svg' + }), + new CometChatMessageComposerAction({ + id: 'copy', + title: 'Copy Text', + iconURL: 'assets/icons/copy.svg' + }), + new CometChatMessageComposerAction({ + id: 'edit', + title: 'Edit', + iconURL: 'assets/icons/edit.svg' + }), + new CometChatMessageComposerAction({ + id: 'delete', + title: 'Delete', + iconURL: 'assets/icons/delete.svg' + }) + ]; + + handleContextAction(action: CometChatMessageComposerAction): void { + console.log('Context action:', action.id); + this.showActions = false; + + switch (action.id) { + case 'reply': + this.replyToMessage(); + break; + case 'forward': + this.forwardMessage(); + break; + case 'copy': + this.copyMessageText(); + break; + case 'edit': + this.editMessage(); + break; + case 'delete': + this.deleteMessage(); + break; + } + } + + replyToMessage(): void { + // Implementation + } + + forwardMessage(): void { + // Implementation + } + + copyMessageText(): void { + // Implementation + } + + editMessage(): void { + // Implementation + } + + deleteMessage(): void { + // Implementation + } +} +``` + +### Attachment Options + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatActionSheetComponent } from '@cometchat/chat-uikit-angular'; +import { CometChatMessageComposerAction } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-attachment-menu', + standalone: true, + imports: [CometChatActionSheetComponent], + template: ` + @if (showAttachments) { + + + } + + + ` +}) +export class AttachmentMenuComponent { + showAttachments = false; + + attachmentOptions: CometChatMessageComposerAction[] = [ + new CometChatMessageComposerAction({ + id: 'photo', + title: 'Photo & Video', + iconURL: 'assets/icons/photo.svg' + }), + new CometChatMessageComposerAction({ + id: 'document', + title: 'Document', + iconURL: 'assets/icons/document.svg' + }), + new CometChatMessageComposerAction({ + id: 'audio', + title: 'Audio', + iconURL: 'assets/icons/audio.svg' + }), + new CometChatMessageComposerAction({ + id: 'location', + title: 'Location', + iconURL: 'assets/icons/location.svg' + }), + new CometChatMessageComposerAction({ + id: 'poll', + title: 'Poll', + iconURL: 'assets/icons/poll.svg' + }) + ]; + + handleAttachment(action: CometChatMessageComposerAction): void { + this.showAttachments = false; + + switch (action.id) { + case 'photo': + this.openPhotoVideoPicker(); + break; + case 'document': + this.openDocumentPicker(); + break; + case 'audio': + this.openAudioPicker(); + break; + case 'location': + this.openLocationPicker(); + break; + case 'poll': + this.openPollCreator(); + break; + } + } + + openPhotoVideoPicker(): void { + // Implementation + } + + openDocumentPicker(): void { + // Implementation + } + + openAudioPicker(): void { + // Implementation + } + + openLocationPicker(): void { + // Implementation + } + + openPollCreator(): void { + // Implementation + } +} +``` + +### Dynamic Actions Based on Permissions + +```typescript expandable +import { Component, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CometChatActionSheetComponent } from '@cometchat/chat-uikit-angular'; +import { CometChatMessageComposerAction } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-permission-based-actions', + standalone: true, + imports: [CommonModule, CometChatActionSheetComponent], + template: ` + @if (showActions) { + + + } + ` +}) +export class PermissionBasedActionsComponent implements OnInit { + showActions = false; + availableActions: CometChatMessageComposerAction[] = []; + + private allActions: CometChatMessageComposerAction[] = [ + new CometChatMessageComposerAction({ + id: 'view', + title: 'View Details', + iconURL: 'assets/icons/view.svg' + }), + new CometChatMessageComposerAction({ + id: 'edit', + title: 'Edit', + iconURL: 'assets/icons/edit.svg' + }), + new CometChatMessageComposerAction({ + id: 'delete', + title: 'Delete', + iconURL: 'assets/icons/delete.svg' + }), + new CometChatMessageComposerAction({ + id: 'share', + title: 'Share', + iconURL: 'assets/icons/share.svg' + }) + ]; + + ngOnInit(): void { + this.updateAvailableActions(); + } + + updateAvailableActions(): void { + const userPermissions = this.getUserPermissions(); + + this.availableActions = this.allActions.filter(action => { + switch (action.id) { + case 'view': + return true; // Everyone can view + case 'edit': + return userPermissions.canEdit; + case 'delete': + return userPermissions.canDelete; + case 'share': + return userPermissions.canShare; + default: + return false; + } + }); + } + + getUserPermissions() { + // Mock permissions - replace with actual permission check + return { + canEdit: true, + canDelete: false, + canShare: true + }; + } + + handleAction(action: CometChatMessageComposerAction): void { + console.log('Action:', action.id); + this.showActions = false; + } +} +``` + +## Best Practices + + + Always provide unique `id` values for each action to ensure proper tracking and event handling. + + + + The ActionSheet doesn't automatically close after an action is selected. Handle the `actionItemClick` event and close the sheet manually by removing it from the DOM or hiding it. + + + + Icons are displayed using CSS mask, which allows them to inherit the icon color from CSS variables. Use SVG icons for best results. + + + + Listen to the `closeSheet` event to handle Escape key presses and provide a consistent close behavior. + + + + Ensure the parent component manages focus restoration when the ActionSheet closes, especially for accessibility. + + +## Integration Patterns + +### With Popover/Overlay + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatActionSheetComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-action-popover', + standalone: true, + imports: [CometChatActionSheetComponent], + template: ` +

+ `, + styles: [` + .popover-backdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.3); + z-index: 999; + } + .popover-content { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 1000; + animation: slideUp 0.3s ease-out; + } + @keyframes slideUp { + from { + transform: translateY(100%); + } + to { + transform: translateY(0); + } + } + `] +}) +export class ActionPopoverComponent { + isOpen = false; + actions = [/* ... */]; + + handleAction(action: any): void { + // Handle action + this.close(); + } + + close(): void { + this.isOpen = false; + } +} +``` + +## Related Components + +- **CometChatButton**: Often used to trigger the ActionSheet +- **CometChatContextMenu**: Alternative menu component with different positioning +- **CometChatMessageComposer**: Uses ActionSheet for attachment options +- **CometChatConfirmDialog**: Similar modal pattern for confirmations + +## Technical Details + +- **Standalone Component**: Can be imported and used independently +- **Change Detection**: Uses default change detection strategy +- **Dependencies**: Only requires Angular CommonModule +- **Bundle Size**: Minimal footprint (~4KB) +- **BEM CSS**: Follows Block Element Modifier naming convention +- **Accessibility**: WCAG 2.1 Level AA compliant + + +For troubleshooting tips, see the [Troubleshooting Guide](/troubleshooting#cometchatactionsheet). + + + diff --git a/ui-kit/angular/components/avatar.mdx b/ui-kit/angular/components/avatar.mdx new file mode 100644 index 000000000..1c177efd3 --- /dev/null +++ b/ui-kit/angular/components/avatar.mdx @@ -0,0 +1,380 @@ +--- +title: Avatar +description: "Display user avatars with automatic fallback to initials" +--- +The `CometChatAvatar` component displays a user's avatar image or automatically generates initials from their name as a fallback. It provides a consistent visual representation of users throughout your chat application. + +## Overview + +The Avatar component intelligently handles image loading and provides graceful fallbacks: + +- **Image Display**: Shows the provided avatar image when available +- **Automatic Fallback**: Displays initials when image fails to load or is unavailable +- **Accessibility**: Includes proper ARIA attributes for screen readers +- **Customizable**: Supports CSS variable-based styling + +## Basic Usage + +### Simple Avatar with Image + +```html + + +``` + +### Avatar with Name Only (Shows Initials) + +```html + +``` + +### Using with CometChat User Object + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatAvatarComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-user-profile', + standalone: true, + imports: [CometChatAvatarComponent], + template: ` + + + ` +}) +export class UserProfileComponent { + user: CometChat.User | null = null; + + ngOnInit() { + // Fetch user data + CometChat.getUser('user-id').then( + (user: CometChat.User) => { + this.user = user; + } + ); + } +} +``` + + + **Live Preview** — interact with the default avatar. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-avatar--default) + + + + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `image` | `string` | `''` | URL of the avatar image to display | +| `name` | `string` | `''` | Name used for generating initials when image is unavailable | + +## Behavior + +### Initials Generation + +The component automatically generates initials from the provided name: + +- **Multiple Words**: First character of first word + first character of second word + - Example: "John Doe" → "JD" +- **Single Word**: First two characters + - Example: "Alice" → "AL" +- **Empty Name**: Shows empty avatar with background color + +All initials are displayed in uppercase. + +### Image Loading + +The component handles image loading gracefully: + +1. If `image` URL is provided, attempts to load the image +2. If image fails to load (404, network error, etc.), automatically falls back to initials +3. If no `image` is provided, displays initials immediately + +## Customization + +### Styling with CSS Variables + +The Avatar component uses CSS variables for easy customization: + +```css +/* Custom avatar styling */ +cometchat-avatar { + --cometchat-extended-primary-color-500: #4A90E2; + --cometchat-primary-button-icon: #FFFFFF; + --cometchat-radius-max: 8px; /* Square corners */ + --cometchat-font-heading3-bold: 700 18px 'Inter'; +} +``` + +### Available CSS Variables + +| Variable | Purpose | Default | +|----------|---------|---------| +| `--cometchat-extended-primary-color-500` | Background color for initials | `#AA9EE8` | +| `--cometchat-primary-button-icon` | Text color for initials | `#FFF` | +| `--cometchat-radius-max` | Border radius | `1000px` (circular) | +| `--cometchat-font-heading3-bold` | Font for initials | `700 20px Roboto` | + +### Custom Sizing + +```css expandable +/* Large avatar */ +.large-avatar cometchat-avatar { + width: 80px; + height: 80px; +} + +/* Small avatar */ +.small-avatar cometchat-avatar { + width: 32px; + height: 32px; +} +``` + +### Custom Colors + +```css expandable +/* Different color schemes */ +.avatar-blue cometchat-avatar { + --cometchat-extended-primary-color-500: #2196F3; +} + +.avatar-green cometchat-avatar { + --cometchat-extended-primary-color-500: #4CAF50; +} + +.avatar-orange cometchat-avatar { + --cometchat-extended-primary-color-500: #FF9800; +} +``` + + + **Live Preview** — all avatar variations side by side. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-avatar--all-variants-showcase) + + + + +## Accessibility + +The Avatar component is fully accessible and follows WCAG 2.1 Level AA guidelines for non-interactive display components. + +### WCAG 2.1 Compliance + +The component meets the following WCAG 2.1 Level AA success criteria: + +- ✅ **1.1.1 Non-text Content (Level A)**: All images have text alternatives +- ✅ **1.3.1 Info and Relationships (Level A)**: Proper semantic structure with ARIA roles +- ✅ **1.4.3 Contrast (Level AA)**: Default colors meet minimum contrast ratios +- ✅ **4.1.2 Name, Role, Value (Level A)**: All elements have accessible names and roles + +### ARIA Attributes + +The component automatically applies appropriate ARIA attributes: + +| Attribute | Element | Value | Purpose | +|-----------|---------|-------|---------| +| `role` | Container | `"img"` | Identifies the avatar as an image element | +| `aria-label` | Container | `"Avatar for {name}"` or `"Avatar"` | Provides accessible name for screen readers | +| `alt` | Image | `{name}` or `"avatar"` | Alternative text for the image | +| `aria-hidden` | Initials span | `"true"` | Hides decorative text from screen readers (label already provided on container) | + +### Screen Reader Behavior + +When a screen reader encounters the Avatar component: + +1. **With name**: Announces "Avatar for John Doe, image" +2. **Without name**: Announces "Avatar, image" +3. **Initials display**: Initials are hidden from screen readers to avoid redundant announcements + +### Non-Interactive Component + +The Avatar component is **non-interactive** by design: + +- ❌ **No keyboard navigation required**: Component is purely presentational +- ❌ **Not focusable**: No interactive elements to receive focus +- ✅ **Screen reader accessible**: Properly announced with semantic information + +If you need an interactive avatar (e.g., clickable to view profile), wrap it in a button: + +```html expandable + +``` + +### High Contrast Mode Support + +The component works well in high contrast mode. For enhanced visibility, you can add a border: + +```css +@media (prefers-contrast: high) { + cometchat-avatar { + border: 2px solid currentColor; + } +} +``` + +### Accessibility Best Practices + + + Always provide the `name` property, even when an image is available. This ensures screen readers can announce meaningful information if the image fails to load. + + + + If you make the avatar clickable by wrapping it in a button or link, ensure the wrapper has an appropriate `aria-label` that describes the action (e.g., "View profile of John Doe"). + + + + The component automatically handles all accessibility requirements. No additional ARIA attributes or keyboard handling is needed for the avatar itself. + + +## Examples + +### User List with Avatars + +```typescript expandable +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CometChatAvatarComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-user-list', + standalone: true, + imports: [CommonModule, CometChatAvatarComponent], + template: ` +
+ @for (user of users; track user.id) { +
+ + + {{ user.name }} +
+ } +
+ `, + styles: [` + .user-list { + display: flex; + flex-direction: column; + gap: 12px; + } + .user-item { + display: flex; + align-items: center; + gap: 12px; + } + `] +}) +export class UserListComponent { + users = [ + { id: 1, name: 'John Doe', avatar: 'https://example.com/john.jpg' }, + { id: 2, name: 'Jane Smith', avatar: '' }, + { id: 3, name: 'Bob Wilson', avatar: 'https://example.com/bob.jpg' } + ]; +} +``` + +### Avatar with Status Indicator + +```typescript expandable +@Component({ + selector: 'app-user-avatar-with-status', + standalone: true, + imports: [CometChatAvatarComponent], + template: ` +
+ + + +
+ `, + styles: [` + .avatar-container { + position: relative; + display: inline-block; + } + .status-indicator { + position: absolute; + bottom: 2px; + right: 2px; + width: 12px; + height: 12px; + border-radius: 50%; + background: #ccc; + border: 2px solid white; + } + .status-indicator.online { + background: #4CAF50; + } + `] +}) +export class UserAvatarWithStatusComponent { + user = { + name: 'John Doe', + avatar: 'https://example.com/avatar.jpg', + isOnline: true + }; +} +``` + +## Best Practices + + + Always provide both `image` and `name` properties. This ensures a good user experience even when images fail to load. + + + + The component automatically handles image loading errors. Don't wrap it in additional error handling unless you need custom behavior. + + + + For consistent sizing across your application, define avatar size classes in your global styles rather than inline styles. + + +## Related Components + +- **CometChatListItem**: Uses Avatar component for displaying user/group items +- **CometChatMessageHeader**: Displays avatar in message conversation headers +- **CometChatUsers**: Shows avatars in user lists +- **CometChatGroups**: Displays group avatars + +## Technical Details + +- **Standalone Component**: Can be imported and used independently +- **Change Detection**: Uses default change detection strategy +- **Dependencies**: Only requires Angular CommonModule +- **Bundle Size**: Minimal footprint (~2KB) diff --git a/ui-kit/angular/components/button.mdx b/ui-kit/angular/components/button.mdx new file mode 100644 index 000000000..8fc53be7e --- /dev/null +++ b/ui-kit/angular/components/button.mdx @@ -0,0 +1,681 @@ +--- +title: Button +description: "A reusable button component with consistent styling, loading states, and accessibility support" +--- +The `CometChatButton` component provides a consistent, accessible button implementation across the UIKit. It supports text labels, icons, loading states, disabled states, and full keyboard accessibility. + +## Overview + +The Button component is designed for uniform interaction patterns throughout your chat application: + +- **Flexible Content**: Supports text, icons, or both +- **Loading State**: Built-in loading animation with automatic disabling +- **Accessibility**: Full keyboard support with Enter/Space activation +- **Customizable**: CSS variable-based styling for easy theming +- **Responsive**: Hover and focus states with visual feedback + +## Basic Usage + +### Simple Text Button + +```html + + +``` + +### Button with Icon + +```html + + +``` + +### Icon-Only Button + +```html + + +``` + +### Disabled Button + +```html + + +``` + +### Loading Button + +```html + + +``` + + + **Live Preview** — interact with the default button. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/base-elements-button--default) + + + + +## Complete Example + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatButtonComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-message-actions', + standalone: true, + imports: [CometChatButtonComponent], + template: ` +
+ + + + + +
+ `, + styles: [` + .message-actions { + display: flex; + gap: 8px; + align-items: center; + } + `] +}) +export class MessageActionsComponent { + canSend = true; + isSending = false; + + handleSend(event: MouseEvent): void { + this.isSending = true; + // Simulate async operation + setTimeout(() => { + console.log('Message sent!'); + this.isSending = false; + }, 2000); + } + + handleAttach(event: MouseEvent): void { + console.log('Attach file clicked'); + } +} +``` + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `text` | `string` | `undefined` | Button text label. If omitted, button displays icon only | +| `hoverText` | `string` | `undefined` | Tooltip text shown on hover (uses native `title` attribute) | +| `iconURL` | `string` | `undefined` | URL or path to icon image. Icon is displayed using CSS mask for color control | +| `disabled` | `boolean` | `false` | Disables the button, preventing clicks and showing disabled styling | +| `isLoading` | `boolean` | `false` | Shows loading animation and disables button interaction | +| `ariaLabel` | `string` | `undefined` | Custom ARIA label for accessibility. Useful for icon-only buttons. Falls back to `text` or `hoverText` if not provided | +| `iconOnly` | `boolean` | `false` | Icon-only mode — removes default width and padding for compact icon buttons | + + + **Live Preview** — all button states (default, hover, disabled, loading). + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/base-elements-button--all-variants-showcase) + + + + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `buttonClick` | `EventEmitter` | Emitted when button is clicked (only when not disabled or loading). Provides the native MouseEvent for additional context | + +## Behavior + +### Loading State + +When `isLoading` is `true`: +- Button displays an animated spinning icon +- Button is automatically disabled +- Text and icon are hidden +- Click events are blocked + +```typescript expandable +@Component({ + template: ` + + + ` +}) +export class SaveButtonComponent { + isSaving = false; + + async saveChanges(): Promise { + this.isSaving = true; + try { + await this.apiService.save(); + console.log('Saved successfully'); + } finally { + this.isSaving = false; + } + } +} +``` + +### Disabled State + +When `disabled` is `true`: +- Button shows reduced opacity (0.6) +- Cursor changes to `not-allowed` +- Click events are blocked +- ARIA attribute `aria-disabled="true"` is set + +### Icon Display + +Icons are rendered using CSS mask, allowing them to inherit the button's color scheme: +- Icon color automatically matches text color +- Supports SVG and PNG formats +- Maintains aspect ratio at 24x24px + +## Customization + +### Styling with CSS Variables + +The Button component uses CSS variables for comprehensive theming: + +```css expandable +/* Custom button styling */ +cometchat-button { + --cometchat-primary-button-background: #4A90E2; + --cometchat-extended-primary-color-800: #357ABD; + --cometchat-extended-primary-color-900: #2868A8; + --cometchat-static-white: #FFFFFF; + --cometchat-primary-color: #4A90E2; + --cometchat-font-button-medium: 500 14px 'Inter'; + --cometchat-padding-2: 8px; + --cometchat-padding-5: 20px; + --cometchat-radius-2: 8px; +} +``` + +### Available CSS Variables + +| Variable | Purpose | Default | +|----------|---------|---------| +| `--cometchat-primary-button-background` | Button background color | `#6852D6` | +| `--cometchat-extended-primary-color-800` | Hover background color | `#7A6BD6` | +| `--cometchat-extended-primary-color-900` | Active/pressed background color | `#5D49BE` | +| `--cometchat-static-white` | Text and icon color | `#FFFFFF` | +| `--cometchat-primary-color` | Focus outline color | `#6852D6` | +| `--cometchat-font-button-medium` | Button text font | `500 14px Roboto` | +| `--cometchat-padding-2` | Vertical padding | `8px` | +| `--cometchat-padding-5` | Horizontal padding | `20px` | +| `--cometchat-radius-2` | Border radius | `8px` | + +### Custom Button Sizes + +```css expandable +/* Small button */ +.small-button cometchat-button { + --cometchat-padding-2: 4px; + --cometchat-padding-5: 12px; + --cometchat-font-button-medium: 500 12px Roboto; +} + +.small-button .cometchat-button { + height: 32px; + width: 120px; +} + +/* Large button */ +.large-button cometchat-button { + --cometchat-padding-2: 12px; + --cometchat-padding-5: 28px; + --cometchat-font-button-medium: 500 16px Roboto; +} + +.large-button .cometchat-button { + height: 48px; + width: 200px; +} +``` + +### Custom Color Schemes + +```css expandable +/* Success button */ +.button-success cometchat-button { + --cometchat-primary-button-background: #4CAF50; + --cometchat-extended-primary-color-800: #45A049; + --cometchat-extended-primary-color-900: #3D8B40; + --cometchat-primary-color: #4CAF50; +} + +/* Danger button */ +.button-danger cometchat-button { + --cometchat-primary-button-background: #F44336; + --cometchat-extended-primary-color-800: #E53935; + --cometchat-extended-primary-color-900: #D32F2F; + --cometchat-primary-color: #F44336; +} + +/* Secondary button */ +.button-secondary cometchat-button { + --cometchat-primary-button-background: #757575; + --cometchat-extended-primary-color-800: #616161; + --cometchat-extended-primary-color-900: #424242; + --cometchat-primary-color: #757575; +} +``` + +### Icon-Only Button Styling + +```css +/* Circular icon button */ +.icon-button .cometchat-button { + width: 40px; + height: 40px; + padding: 8px; + border-radius: 50%; +} +``` + +## Accessibility + +The Button component is fully accessible and follows WCAG 2.1 Level AA guidelines. + +### WCAG 2.1 Compliance + +The component meets the following WCAG 2.1 Level AA success criteria: + +- ✅ **2.1.1 Keyboard (Level A)**: All functionality available via keyboard +- ✅ **2.1.2 No Keyboard Trap (Level A)**: Focus can move away from button +- ✅ **2.4.7 Focus Visible (Level AA)**: Clear focus indicators provided +- ✅ **4.1.2 Name, Role, Value (Level A)**: Proper semantic button element with ARIA attributes + +### Keyboard Support + +| Key | Action | +|-----|--------| +| `Tab` | Moves focus to/from the button | +| `Shift + Tab` | Moves focus backward | +| `Enter` | Activates the button | +| `Space` | Activates the button | + +Both Enter and Space keys trigger the same `buttonClick` event, ensuring consistent behavior across different keyboard navigation patterns. + +### ARIA Attributes + +The component automatically applies appropriate ARIA attributes: + +| Attribute | Value | Purpose | +|-----------|-------|---------| +| `type` | `"button"` | Identifies button type (prevents form submission) | +| `aria-disabled` | `"true"` when disabled/loading | Announces disabled state to screen readers | +| `aria-busy` | `"true"` when loading | Announces loading state to screen readers | +| `aria-label` | Custom or derived from text | Provides accessible name for icon-only buttons | +| `title` | Value of `hoverText` | Provides tooltip for additional context | + +### Custom ARIA Labels + +For icon-only buttons, you can provide a custom `aria-label`: + +```html + + +``` + +If no `ariaLabel` is provided, the component uses this priority: +1. Custom `ariaLabel` input +2. `text` property +3. `hoverText` property +4. Native button content + +### Focus Indicators + +The button provides clear visual focus indicators that meet WCAG contrast requirements: + +- **Focus ring**: 2px solid outline in primary color +- **Offset**: 2px from button edge for clear separation +- **Visibility**: Only shown during keyboard navigation (`:focus-visible`) +- **Mouse clicks**: Focus ring hidden for mouse interactions + +```css +/* Built-in focus styling */ +.cometchat-button:focus-visible { + outline: 2px solid var(--cometchat-primary-color, #6852D6); + outline-offset: 2px; +} +``` + +### Loading State Announcements + +When the button enters loading state: +- Visual loading animation is displayed +- `aria-busy="true"` announces loading to screen readers +- Screen reader-only text "Loading..." is added +- Button is automatically disabled to prevent interaction + +### Screen Reader Behavior + +Screen readers announce the button with: +1. **Button role**: "Button" +2. **Label**: Text, aria-label, or hoverText +3. **State**: "disabled" when disabled, "busy" when loading +4. **Action hint**: "Press Enter or Space to activate" + +Example announcements: +- Text button: "Send message, button" +- Icon button: "Attach file, button" +- Disabled: "Submit, button, disabled" +- Loading: "Saving, button, busy, Loading" + +### Accessibility Best Practices + + + Always provide meaningful labels for icon-only buttons using either `hoverText` or `ariaLabel` to ensure screen reader users understand the button's purpose. + + + + Don't rely solely on color to convey button state. The component uses opacity, cursor changes, and ARIA attributes to communicate disabled/loading states accessibly. + + + + The component uses a native ` + + + `, +}) +export class CustomErrorHistoryComponent { + agentUser: CometChat.User | null = null; + + onMessageClick(message: CometChat.TextMessage): void {} + + reload(): void { + // Re-mount the component or trigger a manual refresh + window.location.reload(); + } +} +``` + +### Auto-Resume Last Conversation + +Set `loadLastAgentConversation` to `true` to automatically pick up where the user left off: + +```typescript +@Component({ + template: ` + @if (agentUser) { + + } + `, +}) +export class AutoResumeComponent { + agentUser: CometChat.User | null = null; + + onMessageClick(message: CometChat.TextMessage): void { + // This fires automatically on first load with the most recent message + console.log('Auto-resumed conversation:', message.getId()); + } +} +``` + +### Localization Keys + +| Key | Default (en-us) | +|-----|-----------------| +| `ai_assistant_chat_history_title` | "AI Assistant" | +| `ai_assistant_chat_history_list_title` | "Chat History" | +| `ai_assistant_chat_history_empty_title` | "No conversations yet" | +| `ai_assistant_chat_history_empty_subtitle` | "Start a new chat to get going" | +| `ai_assistant_chat_history_error_title` | "Couldn't load history" | +| `ai_assistant_chat_history_error_subtitle` | "Please try again" | +| `ai_assistant_chat_new_chat` | "New Chat" | diff --git a/ui-kit/angular/v5/components/cometchat-ai-assistant-chat.mdx b/ui-kit/angular/components/cometchat-ai-assistant-chat.mdx similarity index 100% rename from ui-kit/angular/v5/components/cometchat-ai-assistant-chat.mdx rename to ui-kit/angular/components/cometchat-ai-assistant-chat.mdx diff --git a/ui-kit/angular/v5/components/cometchat-ai-assistant-message-bubble.mdx b/ui-kit/angular/components/cometchat-ai-assistant-message-bubble.mdx similarity index 100% rename from ui-kit/angular/v5/components/cometchat-ai-assistant-message-bubble.mdx rename to ui-kit/angular/components/cometchat-ai-assistant-message-bubble.mdx diff --git a/ui-kit/angular/v5/components/cometchat-audio-bubble.mdx b/ui-kit/angular/components/cometchat-audio-bubble.mdx similarity index 99% rename from ui-kit/angular/v5/components/cometchat-audio-bubble.mdx rename to ui-kit/angular/components/cometchat-audio-bubble.mdx index 1251e189a..54346bdd9 100644 --- a/ui-kit/angular/v5/components/cometchat-audio-bubble.mdx +++ b/ui-kit/angular/components/cometchat-audio-bubble.mdx @@ -668,6 +668,6 @@ Screen readers announce the audio bubble with: -For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/v5/troubleshooting#cometchataudiobubble). +For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchataudiobubble). diff --git a/ui-kit/angular/v5/components/cometchat-call-bubble.mdx b/ui-kit/angular/components/cometchat-call-bubble.mdx similarity index 100% rename from ui-kit/angular/v5/components/cometchat-call-bubble.mdx rename to ui-kit/angular/components/cometchat-call-bubble.mdx diff --git a/ui-kit/angular/v5/components/cometchat-call-buttons.mdx b/ui-kit/angular/components/cometchat-call-buttons.mdx similarity index 94% rename from ui-kit/angular/v5/components/cometchat-call-buttons.mdx rename to ui-kit/angular/components/cometchat-call-buttons.mdx index e3a6ffc16..a7d0f41fc 100644 --- a/ui-kit/angular/v5/components/cometchat-call-buttons.mdx +++ b/ui-kit/angular/components/cometchat-call-buttons.mdx @@ -73,7 +73,7 @@ export class CallButtonsDemoComponent { | `onError` | `((error: CometChat.CometChatException) => void) \| null` | `null` | Error callback invoked for any error during call operations. | | `outgoingCallDisableSoundForCalls` | `boolean` | `false` | Disables sound for the outgoing call overlay. Supports global config override. | | `outgoingCallCustomSoundForCalls` | `string` | `''` | Custom sound URL for the outgoing call overlay. Supports global config override. | -| `callSettingsBuilder` | `CallSettingsBuilder` | `undefined` | Custom `CallSettingsBuilder` to override the default call settings used in the ongoing call screen. Follows the three-tier priority: @Input > [GlobalConfig](/ui-kit/angular/v5/customization/global-config) > default. | +| `callSettingsBuilder` | `CallSettingsBuilder` | `undefined` | Custom `CallSettingsBuilder` to override the default call settings used in the ongoing call screen. Follows the three-tier priority: @Input > [GlobalConfig](/ui-kit/angular/customization/global-config) > default. | | `voiceCallButtonView` | `TemplateRef \| null` | `null` | Replaces the default voice call button with a custom template. | | `videoCallButtonView` | `TemplateRef \| null` | `null` | Replaces the default video call button with a custom template. | @@ -147,7 +147,7 @@ export class CustomCallSettingsComponent implements OnInit { ``` - You can also set `callSettingsBuilder` globally via [GlobalConfig](/ui-kit/angular/v5/customization/global-config) so all call components use the same settings without passing the input to each one. + You can also set `callSettingsBuilder` globally via [GlobalConfig](/ui-kit/angular/customization/global-config) so all call components use the same settings without passing the input to each one. ## Accessibility @@ -168,5 +168,5 @@ export class CustomCallSettingsComponent implements OnInit { ## Related Components - [CometChatOutgoingCall](./cometchat-outgoing-call.mdx) — Displayed automatically when a call is initiated -- [CometChatOngoingCall](/ui-kit/angular/v5/components/cometchat-outgoing-call) — Displayed automatically when a call is accepted +- [CometChatOngoingCall](/ui-kit/angular/components/cometchat-outgoing-call) — Displayed automatically when a call is accepted - [CometChatMessageHeader](./cometchat-message-header.mdx) — Typically hosts the call buttons diff --git a/ui-kit/angular/v5/components/cometchat-call-logs.mdx b/ui-kit/angular/components/cometchat-call-logs.mdx similarity index 98% rename from ui-kit/angular/v5/components/cometchat-call-logs.mdx rename to ui-kit/angular/components/cometchat-call-logs.mdx index a1db56c8b..822588078 100644 --- a/ui-kit/angular/v5/components/cometchat-call-logs.mdx +++ b/ui-kit/angular/components/cometchat-call-logs.mdx @@ -132,7 +132,7 @@ When `callButtonClicked` has no observers, clicking the trailing call button aut | `callLogRequestBuilder` | `any` | `null` | Custom `CallLogRequestBuilder` from `CometChatCalls`. When provided, overrides the default builder (limit 30, category "call"). | | `callInitiatedDateTimeFormat` | `CalendarObject \| null` | `null` | Custom date format for the call initiation timestamp. Merged with global and default `CalendarObject` formats (component-level wins). | | `showScrollbar` | `boolean` | `false` | Whether to show the scrollbar on the call logs list. | -| `callSettingsBuilder` | `CallSettingsBuilder` | `undefined` | Custom `CallSettingsBuilder` to override the default call settings used in the ongoing call overlay. Follows the three-tier priority: @Input > [GlobalConfig](/ui-kit/angular/v5/customization/global-config) > default. | +| `callSettingsBuilder` | `CallSettingsBuilder` | `undefined` | Custom `CallSettingsBuilder` to override the default call settings used in the ongoing call overlay. Follows the three-tier priority: @Input > [GlobalConfig](/ui-kit/angular/customization/global-config) > default. | | `onError` | `((error: CometChat.CometChatException) => void) \| null` | `null` | Error callback invoked when any error occurs during operations such as fetching call logs or initiating calls. | ### Template Properties @@ -333,7 +333,7 @@ export class CustomCallSettingsLogsComponent implements OnInit { ``` - You can also set `callSettingsBuilder` globally via [GlobalConfig](/ui-kit/angular/v5/customization/global-config) so all call components use the same settings without passing the input to each one. + You can also set `callSettingsBuilder` globally via [GlobalConfig](/ui-kit/angular/customization/global-config) so all call components use the same settings without passing the input to each one. ## Customization with Templates @@ -678,7 +678,7 @@ Errors are handled at two levels: When a fetch error occurs and no call logs have been loaded yet, the component transitions to the error state and displays the built-in error view (or your custom `errorView` template). -For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/v5/troubleshooting#cometchatcalllogs). +For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchatcalllogs). ## Related Components diff --git a/ui-kit/angular/components/cometchat-change-scope.mdx b/ui-kit/angular/components/cometchat-change-scope.mdx new file mode 100644 index 000000000..5ca3eee8f --- /dev/null +++ b/ui-kit/angular/components/cometchat-change-scope.mdx @@ -0,0 +1,373 @@ +--- +title: "CometChatChangeScope" +description: "A dialog component for changing group member roles/scopes with radio button selection" +--- + +## Overview + +The `CometChatChangeScope` component provides a dialog interface for changing group member roles or scopes. It uses radio buttons for scope selection and includes loading and error state management for async operations. + +## Basic Usage + +### Basic Example + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatChangeScopeComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-example', + template: ` + + + ` +}) +export class ExampleComponent { + scopeOptions = ['admin', 'moderator', 'participant']; + currentScope = 'participant'; + + onScopeChange(newScope: string) { + console.log('Scope changed to:', newScope); + // Handle async operation here + } + + onClose() { + console.log('Dialog closed'); + } +} +``` + +### With Async Operation Handling + +```typescript expandable +import { Component, ViewChild } from '@angular/core'; +import { CometChatChangeScopeComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-example', + template: ` + + + ` +}) +export class ExampleComponent { + @ViewChild('changeScopeDialog') changeScopeDialog!: CometChatChangeScopeComponent; + + async handleScopeChange(newScope: string) { + try { + // Perform async operation (e.g., API call) + await this.updateMemberScope(newScope); + + // Call setSuccess() on successful operation + this.changeScopeDialog.setSuccess(); + this.closeDialog(); + } catch (error) { + // Call setError() on failed operation + this.changeScopeDialog.setError(); + } + } + + async updateMemberScope(scope: string): Promise { + // Your API call here + return new Promise((resolve) => setTimeout(resolve, 1000)); + } + + closeDialog() { + // Handle dialog close + } +} +``` + + + **Live Preview** — interact with the default change scope selector. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/base-elements-change-scope--default) + + + + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `title` | `string` | `'Change Scope'` | Title text displayed in the dialog header | +| `buttonText` | `string` | `'Confirm'` | Text for the confirm/submit button | +| `options` | `string[]` | `[]` | Array of available scope options to display as radio buttons | +| `defaultSelection` | `string` | `''` | Currently selected scope (default value) | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `scopeChanged` | `string` | Emitted when the confirm button is clicked. Returns the selected scope value. Parent should handle async operation and call `setSuccess()` or `setError()` | +| `closeClick` | `void` | Emitted when the close button or cancel button is clicked | + +## Methods + +| Method | Parameters | Description | +|--------|------------|-------------| +| `setSuccess()` | None | Call this method after successful async operation to clear loading state | +| `setError()` | None | Call this method after failed async operation to show error message | + +## Customization + +### Styling with CSS Variables + +```css expandable +/* Dialog background and border */ +--cometchat-background-color: #ffffff; +--cometchat-border-color: #e0e0e0; +--cometchat-border-radius: 8px; +--cometchat-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + +/* Text colors */ +--cometchat-text-color: #333333; +--cometchat-text-color-secondary: #666666; + +/* Interactive elements */ +--cometchat-primary-color: #6851ff; +--cometchat-hover-background: #f5f5f5; + +/* Error state */ +--cometchat-error-background: #ffebee; +--cometchat-error-color: #c62828; +``` + +### Example with Custom Styling + +```typescript expandable +@Component({ + selector: 'app-example', + template: ` +
+ + +
+ `, + styles: [` + .custom-change-scope { + --cometchat-primary-color: #2196F3; + --cometchat-border-radius: 12px; + --cometchat-background-color: #fafafa; + } + `] +}) +export class ExampleComponent { + roles = ['Owner', 'Admin', 'Member', 'Guest']; + currentRole = 'Member'; + + onRoleChange(newRole: string) { + console.log('Role changed to:', newRole); + } + + onCancel() { + console.log('Cancelled'); + } +} +``` + + + **Live Preview** — change scope with descriptive option labels. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/base-elements-change-scope--available-scope-options) + + + + +## Accessibility + +The `CometChatChangeScope` component is built with comprehensive WCAG 2.1 Level AA accessibility support: + +### Keyboard Navigation + +The component supports full keyboard navigation for users who cannot use a mouse: + +| Key | Action | Context | +|-----|--------|---------| +| **Tab** | Move focus forward through interactive elements | Navigate between radio buttons, Cancel button, and Confirm button | +| **Shift + Tab** | Move focus backward through interactive elements | Reverse navigation through the dialog | +| **Arrow Keys** (↑↓) | Navigate between radio button options | When focus is on any radio button in the group | +| **Space** | Select focused radio button or activate focused button | When focus is on a radio button or button element | +| **Enter** | Activate focused button | When focus is on Cancel or Confirm button | +| **Escape** | Close the dialog | Works from anywhere within the dialog, including during loading state | + +### Screen Reader Support + +The component provides comprehensive screen reader support: + +- **Dialog Role**: The container has `role="dialog"` and `aria-modal="true"` to announce it as a modal dialog +- **Dialog Label**: The dialog is labeled with `aria-labelledby` pointing to the title for context +- **Radio Group**: The options container has `role="radiogroup"` with `aria-label` for proper announcement +- **Radio Buttons**: Each option uses native radio button semantics with proper labels +- **Error Announcements**: Error messages use `role="alert"` for immediate screen reader announcement +- **Button Labels**: The close button has `aria-label="Close"` for screen reader users +- **Loading State**: The Confirm button announces loading state via `aria-busy` attribute + +### Focus Management + +The component implements proper focus management for keyboard accessibility: + +- **Auto-Focus**: When the dialog opens, focus automatically moves to the first radio button +- **Focus Indicators**: All interactive elements have visible focus indicators using `:focus-visible` +- **Focus Order**: Logical tab order follows visual layout: + 1. Close button (×) + 2. Radio button options (with arrow key navigation) + 3. Cancel button + 4. Confirm button +- **Focus Containment**: Focus remains within the dialog while it's open +- **Escape Key**: Pressing Escape closes the dialog from any focused element + +### Visual Focus Indicators + +All interactive elements have clear visual focus indicators: + +- **Close Button**: 2px solid outline in primary color with 2px offset +- **Radio Buttons**: Handled by `CometChatRadioButton` component with proper focus styles +- **Action Buttons**: Handled by `CometChatButton` component with proper focus styles +- **Keyboard-Only**: Focus indicators only appear for keyboard navigation (`:focus-visible`), not mouse clicks + +### ARIA Attributes + +The component uses the following ARIA attributes for accessibility: + +| Element | ARIA Attributes | Purpose | +|---------|----------------|---------| +| Dialog container | `role="dialog"`, `aria-modal="true"`, `aria-labelledby` | Identifies as modal dialog with title reference | +| Title | `id="change-scope-title"` | Provides accessible name for dialog | +| Radio group | `role="radiogroup"`, `aria-label` | Groups radio buttons with descriptive label | +| Error message | `role="alert"` | Announces errors immediately to screen readers | +| Close button | `aria-label="Close"` | Provides accessible name for icon-only button | +| Confirm button | `aria-busy` (when loading) | Announces loading state | + +### Accessibility Best Practices + +When using this component, follow these best practices: + +1. **Provide Clear Title**: Use a descriptive title that explains the purpose of the dialog +2. **Descriptive Options**: Use clear, understandable labels for scope options +3. **Error Handling**: Implement proper error handling and call `setError()` on failures +4. **Focus Restoration**: When closing the dialog, restore focus to the element that opened it +5. **Keyboard Testing**: Test the component with keyboard-only navigation +6. **Screen Reader Testing**: Test with screen readers (NVDA, JAWS, VoiceOver) + +### Testing Accessibility + +To verify accessibility: + +```typescript expandable +// Test keyboard navigation +it('should support full keyboard navigation', () => { + // Tab through all interactive elements + // Use arrow keys to navigate radio options + // Press Escape to close + // Verify focus indicators are visible +}); + +// Test screen reader announcements +it('should announce changes to screen readers', () => { + // Verify dialog role and label + // Verify radiogroup role and label + // Verify error messages use role="alert" +}); +``` + +### Known Limitations + +- **Focus Trap**: The component does not implement a focus trap. Parent components should handle focus trapping if needed for modal contexts. +- **Focus Restoration**: The component emits `closeClick` event but does not automatically restore focus. Parent components should handle focus restoration. + +### WCAG 2.1 Compliance + +This component meets the following WCAG 2.1 Level AA success criteria: + +- **1.3.1 Info and Relationships (Level A)**: Proper semantic HTML and ARIA roles +- **2.1.1 Keyboard (Level A)**: All functionality available via keyboard +- **2.1.2 No Keyboard Trap (Level A)**: Escape key allows exiting the dialog +- **2.4.3 Focus Order (Level A)**: Logical focus order follows visual layout +- **2.4.7 Focus Visible (Level AA)**: Clear focus indicators on all interactive elements +- **3.2.1 On Focus (Level A)**: No unexpected context changes on focus +- **4.1.2 Name, Role, Value (Level A)**: Proper ARIA attributes and semantic HTML +- **4.1.3 Status Messages (Level AA)**: Error messages use `role="alert"` + +## Best Practices + +1. **Handle Async Operations**: Always call `setSuccess()` or `setError()` after async operations complete +2. **Provide Clear Options**: Use descriptive scope names that users will understand +3. **Default Selection**: Always provide a `defaultSelection` that matches the current state +4. **Error Handling**: Implement proper error handling and call `setError()` on failures +5. **Loading State**: The component automatically manages loading state during operations +6. **Disabled State**: Submit button is automatically disabled when selection hasn't changed + +## Common Use Cases + +### Group Member Role Management + +```typescript expandable +changeMemberRole(memberId: string, newRole: string) { + this.showChangeScopeDialog = true; + this.currentMemberId = memberId; + this.scopeOptions = ['admin', 'moderator', 'participant']; + this.currentScope = this.getMemberRole(memberId); +} + +async handleRoleChange(newRole: string) { + try { + await this.groupService.updateMemberRole( + this.currentMemberId, + newRole + ); + this.changeScopeDialog.setSuccess(); + this.showChangeScopeDialog = false; + } catch (error) { + this.changeScopeDialog.setError(); + } +} +``` + +### Permission Level Changes + +```typescript +changePermissionLevel(userId: string) { + this.scopeOptions = ['read', 'write', 'admin']; + this.currentScope = this.getUserPermission(userId); + this.showDialog = true; +} +``` + +## Notes + +- The submit button is automatically disabled when the selected value matches the default selection +- The submit button is also disabled during loading state +- Error messages are displayed below the options and above the action buttons +- The component uses `CometChatRadioButton` and `CometChatButton` components internally +- Loading state is managed internally but requires parent to call `setSuccess()` or `setError()` diff --git a/ui-kit/angular/components/cometchat-checkbox.mdx b/ui-kit/angular/components/cometchat-checkbox.mdx new file mode 100644 index 000000000..61d9a9477 --- /dev/null +++ b/ui-kit/angular/components/cometchat-checkbox.mdx @@ -0,0 +1,697 @@ +--- +title: Checkbox +description: "A reusable checkbox component with indeterminate state support, full keyboard accessibility, and Angular forms integration" +--- +The `CometChatCheckbox` component provides a consistent, accessible checkbox implementation across the UIKit. It supports checked, disabled, and indeterminate states with full keyboard accessibility and Angular forms integration via ControlValueAccessor. + +## Overview + +The Checkbox component is designed for uniform selection patterns throughout your chat application: + +- **Flexible States**: Supports checked, unchecked, disabled, and indeterminate states +- **Forms Integration**: Implements ControlValueAccessor for seamless Angular forms support +- **Accessibility**: Full keyboard support with Space key activation +- **Customizable**: CSS variable-based styling for easy theming +- **Responsive**: Hover and focus states with visual feedback + +## Basic Usage + +### Simple Checkbox + +```html + + +``` + +### Checked Checkbox + +```html + + +``` + +### Disabled Checkbox + +```html + + +``` + +### Indeterminate Checkbox + +```html + + +``` + + + **Live Preview** — interact with the default checkbox. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/base-elements-checkbox--default) + + + + +## Complete Example + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatCheckboxComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-settings-form', + standalone: true, + imports: [CometChatCheckboxComponent], + template: ` +
+

Notification Settings

+ + + + + + + + + +
+ `, + styles: [` + .settings-form { + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px; + } + h3 { + margin: 0 0 8px 0; + font-size: 16px; + font-weight: 600; + } + `] +}) +export class SettingsFormComponent { + settings = { + emailNotifications: true, + pushNotifications: false, + soundEnabled: false + }; + + onEmailNotificationsChange(event: { checked: boolean; labelText: string }): void { + this.settings.emailNotifications = event.checked; + console.log('Email notifications:', event.checked); + } + + onPushNotificationsChange(event: { checked: boolean; labelText: string }): void { + this.settings.pushNotifications = event.checked; + if (!event.checked) { + this.settings.soundEnabled = false; + } + console.log('Push notifications:', event.checked); + } + + onSoundChange(event: { checked: boolean; labelText: string }): void { + this.settings.soundEnabled = event.checked; + console.log('Sound enabled:', event.checked); + } +} +``` + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `checked` | `boolean` | `false` | Checked state of the checkbox | +| `labelText` | `string` | `''` | Label text displayed next to the checkbox | +| `disabled` | `boolean` | `false` | Disables the checkbox, preventing interaction | +| `indeterminate` | `boolean` | `false` | Shows indeterminate state (partial selection, typically used for "select all" scenarios) | +| `ariaLabel` | `string` | `undefined` | Custom aria-label for accessibility (overrides `labelText` for screen readers) | + + + **Live Preview** — all checkbox states (unchecked, checked, disabled, indeterminate). + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/base-elements-checkbox--all-variants-showcase) + + + + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `checkboxChanged` | `EventEmitter<{ checked: boolean; labelText: string }>` | Emitted when checkbox state changes. Provides both the new checked state and the label text | + +## Behavior + +### Checked State + +The checkbox can be controlled or uncontrolled: + +```typescript expandable +// Controlled checkbox +@Component({ + template: ` + + + ` +}) +export class ControlledCheckboxComponent { + isChecked = false; + + onCheckboxChange(event: { checked: boolean; labelText: string }): void { + this.isChecked = event.checked; + } +} +``` + +### Indeterminate State + +The indeterminate state is useful for "select all" scenarios where some but not all items are selected: + +```typescript expandable +@Component({ + template: ` +
+ + + +
+ @for (item of items; track item.id) { + + + } +
+
+ ` +}) +export class SelectAllComponent { + items = [ + { id: 1, name: 'Item 1', selected: false }, + { id: 2, name: 'Item 2', selected: true }, + { id: 3, name: 'Item 3', selected: false } + ]; + + get allSelected(): boolean { + return this.items.every(item => item.selected); + } + + get someSelected(): boolean { + const selectedCount = this.items.filter(item => item.selected).length; + return selectedCount > 0 && selectedCount < this.items.length; + } + + onSelectAllChange(event: { checked: boolean }): void { + this.items.forEach(item => item.selected = event.checked); + } + + onItemChange(item: any, event: { checked: boolean }): void { + item.selected = event.checked; + } +} +``` + +### Disabled State + +When `disabled` is `true`: +- Checkbox shows reduced opacity (0.5) +- Cursor changes to `not-allowed` +- Click events are blocked +- ARIA attribute `aria-disabled="true"` is set + +## Angular Forms Integration + +The checkbox implements `ControlValueAccessor`, allowing seamless integration with Angular forms: + +### Reactive Forms + +```typescript expandable +import { Component } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { CometChatCheckboxComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-reactive-form', + standalone: true, + imports: [ReactiveFormsModule, CometChatCheckboxComponent], + template: ` +
+ + + + + + + +
+ ` +}) +export class ReactiveFormComponent { + form: FormGroup; + + constructor(private fb: FormBuilder) { + this.form = this.fb.group({ + agreeToTerms: [false], + subscribeNewsletter: [true] + }); + } + + onSubmit(): void { + console.log('Form values:', this.form.value); + } +} +``` + +### Template-Driven Forms + +```typescript expandable +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { CometChatCheckboxComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-template-form', + standalone: true, + imports: [FormsModule, CometChatCheckboxComponent], + template: ` +
+ + + + + + + +
+ ` +}) +export class TemplateFormComponent { + model = { + agreeToTerms: false, + subscribeNewsletter: true + }; + + onSubmit(): void { + console.log('Form values:', this.model); + } +} +``` + +## Customization + +### Styling with CSS Variables + +The Checkbox component uses CSS variables for comprehensive theming: + +```css expandable +/* Custom checkbox styling */ +cometchat-checkbox { + --cometchat-primary-color: #4A90E2; + --cometchat-border-color-default: #E0E0E0; + --cometchat-border-color-dark: #BDBDBD; + --cometchat-static-white: #FFFFFF; + --cometchat-text-color-secondary: #424242; + --cometchat-font-body-regular: 400 14px 'Inter'; + --cometchat-radius-1: 4px; + --cometchat-radius-2: 8px; +} +``` + +### Available CSS Variables + +| Variable | Purpose | Default | +|----------|---------|---------| +| `--cometchat-primary-color` | Checked state background and border color | `#6852D6` | +| `--cometchat-border-color-default` | Unchecked border color | `#E8E8E8` | +| `--cometchat-border-color-dark` | Hover border color | `#DCDCDC` | +| `--cometchat-static-white` | Checkmark color | `#FFFFFF` | +| `--cometchat-text-color-secondary` | Label text color | `#424242` | +| `--cometchat-font-body-regular` | Label font | `400 14px Roboto` | +| `--cometchat-radius-1` | Checkbox border radius | `4px` | +| `--cometchat-radius-2` | Label gap spacing | `8px` | + +### Custom Color Schemes + +```css expandable +/* Success checkbox */ +.checkbox-success cometchat-checkbox { + --cometchat-primary-color: #4CAF50; +} + +/* Warning checkbox */ +.checkbox-warning cometchat-checkbox { + --cometchat-primary-color: #FF9800; +} + +/* Error checkbox */ +.checkbox-error cometchat-checkbox { + --cometchat-primary-color: #F44336; +} +``` + +### Custom Sizes + +```css expandable +/* Large checkbox */ +.checkbox-large .cometchat-checkbox__checkmark { + width: 32px; + height: 32px; +} + +.checkbox-large .cometchat-checkbox__checkmark::after { + width: 20px; + height: 20px; +} + +/* Small checkbox */ +.checkbox-small .cometchat-checkbox__checkmark { + width: 18px; + height: 18px; +} + +.checkbox-small .cometchat-checkbox__checkmark::after { + width: 12px; + height: 12px; +} +``` + +## Accessibility + +The Checkbox component includes comprehensive accessibility features: + +### Keyboard Support + +| Key | Action | +|-----|--------| +| `Tab` | Moves focus to/from the checkbox | +| `Space` | Toggles the checkbox state | + +### ARIA Attributes + +- `role="checkbox"`: Explicitly identifies the element as a checkbox +- `aria-checked`: Set to `"true"`, `"false"`, or `"mixed"` (for indeterminate state) +- `aria-disabled`: Set to `"true"` when checkbox is disabled +- `aria-label`: Provides accessible label from `labelText` property + +### Focus Indicators + +The checkbox provides clear visual focus indicators: +- 2px solid outline in primary color +- 2px offset from checkbox edge +- Visible during keyboard navigation + +```css +/* Focus styling is built-in */ +.cometchat-checkbox__input:focus + .cometchat-checkbox__checkmark { + outline: 2px solid var(--cometchat-primary-color, #6852D6); + outline-offset: 2px; +} +``` + +## Examples + +### Checkbox List + +```typescript expandable +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CometChatCheckboxComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-checkbox-list', + standalone: true, + imports: [CommonModule, CometChatCheckboxComponent], + template: ` +
+

Select your interests

+ @for (interest of interests; track interest.id) { + + + } +
+ `, + styles: [` + .checkbox-list { + display: flex; + flex-direction: column; + gap: 12px; + padding: 20px; + } + h3 { + margin: 0 0 8px 0; + } + `] +}) +export class CheckboxListComponent { + interests = [ + { id: 1, name: 'Technology', selected: false }, + { id: 2, name: 'Sports', selected: true }, + { id: 3, name: 'Music', selected: false }, + { id: 4, name: 'Travel', selected: true } + ]; + + onInterestChange(interest: any, event: { checked: boolean }): void { + interest.selected = event.checked; + console.log('Selected interests:', this.getSelectedInterests()); + } + + getSelectedInterests(): string[] { + return this.interests + .filter(i => i.selected) + .map(i => i.name); + } +} +``` + +### Nested Checkboxes with Select All + +```typescript expandable +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CometChatCheckboxComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-nested-checkboxes', + standalone: true, + imports: [CommonModule, CometChatCheckboxComponent], + template: ` +
+ + + +
+ @for (permission of permissions; track permission.id) { + + + } +
+
+ `, + styles: [` + .nested-checkboxes { + padding: 20px; + } + .permission-group { + margin-left: 32px; + margin-top: 12px; + display: flex; + flex-direction: column; + gap: 12px; + } + `] +}) +export class NestedCheckboxesComponent { + permissions = [ + { id: 1, name: 'Read messages', selected: true }, + { id: 2, name: 'Send messages', selected: false }, + { id: 3, name: 'Delete messages', selected: false }, + { id: 4, name: 'Manage users', selected: false } + ]; + + get allPermissionsSelected(): boolean { + return this.permissions.every(p => p.selected); + } + + get somePermissionsSelected(): boolean { + const selectedCount = this.permissions.filter(p => p.selected).length; + return selectedCount > 0 && selectedCount < this.permissions.length; + } + + onSelectAllChange(event: { checked: boolean }): void { + this.permissions.forEach(p => p.selected = event.checked); + } + + onPermissionChange(permission: any, event: { checked: boolean }): void { + permission.selected = event.checked; + } +} +``` + +## Best Practices + + + Always provide meaningful `labelText` to improve accessibility and user experience. + + + + The indeterminate state is cleared automatically when the user clicks the checkbox. You'll need to manage it programmatically for "select all" scenarios. + + + + The checkbox emits both the checked state and label text, making it easy to identify which checkbox changed in lists. + + + + Use the disabled state to prevent interaction with unavailable options, but consider hiding them entirely if they're not relevant to the current context. + + +## Angular Version Compatibility + +This component is compatible with Angular versions 14 through 21. + +### Compatibility Matrix + +| Angular Version | Status | Notes | +|----------------|--------|-------| +| 14.x | ✅ Supported | Standalone components, inject() available | +| 15.x - 21.x | ✅ Supported | All features work | + +### API Stability + +The component uses only stable Angular APIs: +- `@Component`, `@Input`, `@Output` (Angular 2+) +- `@HostListener` (Angular 2+) +- `EventEmitter`, `ElementRef`, `Renderer2` (Angular 2+) +- `ControlValueAccessor` (Angular 2+) +- Standalone components (Angular 14+) + +### Upgrade Path + +No changes required when upgrading from Angular 14 to 21. The component uses stable APIs and follows Angular best practices. + +## Integration and Isolation + +### Event Listener Scoping + +This component uses component-scoped event listeners that don't interfere with your application: + +```typescript +@HostListener('keydown', ['$event']) +handleKeydown(event: KeyboardEvent): void { + // Only handles events within this component + // Space key toggles checkbox +} +``` + +### Focus Management + +Focus management is isolated to this component: +- Native checkbox element handles focus automatically +- Focus indicators only appear during keyboard navigation (`:focus-visible`) +- No global focus manipulation + +### Non-Interference + +The component is designed to not interfere with your application: +- ✅ No global event listeners +- ✅ No document-level DOM manipulation +- ✅ Component-scoped styles with ViewEncapsulation +- ✅ Proper cleanup in `ngOnDestroy()` + +### Multiple Instances + +Multiple instances of this component work independently without interference: + +```typescript +// These work independently + + + +``` + +### Cleanup + +The component properly cleans up all resources when destroyed: +- Event listeners are removed automatically +- No subscriptions to clean up +- No memory leaks + +## Related Components + +- **CometChatRadioButton**: For mutually exclusive selections +- **CometChatButton**: For action triggers +- **CometChatConfirmDialog**: Uses checkboxes for confirmation options + +## Technical Details + +- **Standalone Component**: Can be imported and used independently +- **Change Detection**: Uses default change detection strategy +- **Dependencies**: Only requires Angular CommonModule and FormsModule +- **Bundle Size**: Minimal footprint (~2KB) +- **BEM CSS**: Follows Block Element Modifier naming convention +- **Accessibility**: WCAG 2.1 Level AA compliant +- **Forms Support**: Implements ControlValueAccessor for full Angular forms integration +- **Angular Compatibility**: Works with Angular 14-21 + + diff --git a/ui-kit/angular/v5/components/cometchat-collaborative-document-bubble.mdx b/ui-kit/angular/components/cometchat-collaborative-document-bubble.mdx similarity index 99% rename from ui-kit/angular/v5/components/cometchat-collaborative-document-bubble.mdx rename to ui-kit/angular/components/cometchat-collaborative-document-bubble.mdx index 405986c0a..5156c302e 100644 --- a/ui-kit/angular/v5/components/cometchat-collaborative-document-bubble.mdx +++ b/ui-kit/angular/components/cometchat-collaborative-document-bubble.mdx @@ -462,7 +462,7 @@ Screen readers announce the component with:
-For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/v5/troubleshooting#cometchatcollaborativedocumentbubble). +For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchatcollaborativedocumentbubble). ## Related Components diff --git a/ui-kit/angular/v5/components/cometchat-collaborative-whiteboard-bubble.mdx b/ui-kit/angular/components/cometchat-collaborative-whiteboard-bubble.mdx similarity index 99% rename from ui-kit/angular/v5/components/cometchat-collaborative-whiteboard-bubble.mdx rename to ui-kit/angular/components/cometchat-collaborative-whiteboard-bubble.mdx index b39c7f278..b472ed46f 100644 --- a/ui-kit/angular/v5/components/cometchat-collaborative-whiteboard-bubble.mdx +++ b/ui-kit/angular/components/cometchat-collaborative-whiteboard-bubble.mdx @@ -462,7 +462,7 @@ Screen readers announce the component with: -For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/v5/troubleshooting#cometchatcollaborativewhiteboardbubble). +For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchatcollaborativewhiteboardbubble). ## Related Components diff --git a/ui-kit/angular/v5/components/cometchat-conversation-item.mdx b/ui-kit/angular/components/cometchat-conversation-item.mdx similarity index 100% rename from ui-kit/angular/v5/components/cometchat-conversation-item.mdx rename to ui-kit/angular/components/cometchat-conversation-item.mdx diff --git a/ui-kit/angular/v5/components/cometchat-conversation-starter.mdx b/ui-kit/angular/components/cometchat-conversation-starter.mdx similarity index 94% rename from ui-kit/angular/v5/components/cometchat-conversation-starter.mdx rename to ui-kit/angular/components/cometchat-conversation-starter.mdx index 9fdae5330..b2fb4ceda 100644 --- a/ui-kit/angular/v5/components/cometchat-conversation-starter.mdx +++ b/ui-kit/angular/components/cometchat-conversation-starter.mdx @@ -143,5 +143,5 @@ The Conversation Starter component uses BEM-style CSS classes that can be custom ## Related Components -- [CometChatSmartReplies](/ui-kit/angular/v5/components/cometchat-smart-replies) - AI-generated smart reply suggestions for received messages -- [CometChatMessageList](/ui-kit/angular/v5/components/cometchat-message-list) - Message list that hosts the conversation starter component +- [CometChatSmartReplies](/ui-kit/angular/components/cometchat-smart-replies) - AI-generated smart reply suggestions for received messages +- [CometChatMessageList](/ui-kit/angular/components/cometchat-message-list) - Message list that hosts the conversation starter component diff --git a/ui-kit/angular/v5/components/cometchat-conversation-summary.mdx b/ui-kit/angular/components/cometchat-conversation-summary.mdx similarity index 100% rename from ui-kit/angular/v5/components/cometchat-conversation-summary.mdx rename to ui-kit/angular/components/cometchat-conversation-summary.mdx diff --git a/ui-kit/angular/v5/components/cometchat-conversations.mdx b/ui-kit/angular/components/cometchat-conversations.mdx similarity index 99% rename from ui-kit/angular/v5/components/cometchat-conversations.mdx rename to ui-kit/angular/components/cometchat-conversations.mdx index 9aefd3879..b37a2775a 100644 --- a/ui-kit/angular/v5/components/cometchat-conversations.mdx +++ b/ui-kit/angular/components/cometchat-conversations.mdx @@ -1381,7 +1381,7 @@ Provide a custom error view with retry functionality: ``` -For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/v5/troubleshooting#cometchatconversations). +For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchatconversations). ## Best Practices diff --git a/ui-kit/angular/components/cometchat-create-poll.mdx b/ui-kit/angular/components/cometchat-create-poll.mdx new file mode 100644 index 000000000..91297c586 --- /dev/null +++ b/ui-kit/angular/components/cometchat-create-poll.mdx @@ -0,0 +1,167 @@ +--- +title: "CometChatCreatePoll" +description: "A modal dialog component for creating poll messages with a question and multiple answer options" +--- +The `CometChatCreatePoll` component provides a modal dialog for composing poll messages. Users enter a question and define multiple answer options (2–12), then submit the poll to a user or group conversation. + +## Overview + +The Create Poll component provides: + +- **Question Input**: Text field for the poll question +- **Dynamic Answer Options**: Add or remove answer options (minimum 2, maximum 12) +- **Validation**: Ensures a non-empty question and at least two non-empty options before submission +- **Loading State**: Disables the create button and shows a loading indicator during submission +- **Error Handling**: Displays localized error messages on validation or API failure +- **Focus Trap**: Keeps keyboard focus within the modal while open +- **Focus Restoration**: Returns focus to the previously focused element on close + + + + **Live Preview** — default create poll dialog preview. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-create-poll--default) + + + + +## Basic Usage + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatCreatePollComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-create-poll-demo', + standalone: true, + imports: [CometChatCreatePollComponent], + template: ` + + + ` +}) +export class CreatePollDemoComponent { + selectedUser!: CometChat.User; + selectedGroup!: CometChat.Group; + + onClose(): void { + console.log('Poll dialog closed'); + } + + onPollCreated(): void { + console.log('Poll created successfully'); + } + + onError(error: CometChat.CometChatException): void { + console.error('Poll creation error:', error); + } +} +``` + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `title` | `string` | `undefined` | Custom title for the poll creation form | +| `user` | `CometChat.User` | `undefined` | The user to send the poll to (for 1-on-1 conversations) | +| `group` | `CometChat.Group` | `undefined` | The group to send the poll to (for group conversations) | +| `replyToMessage` | `CometChat.BaseMessage` | `undefined` | Message to reply to (for quoted replies) | +| `defaultAnswers` | `number` | `2` | Default number of answer option fields shown initially (minimum 2) | +| `questionPlaceholderText` | `string` | `undefined` | Custom placeholder text for the question input | +| `answerPlaceholderText` | `string` | `undefined` | Custom placeholder text for answer option inputs | +| `answerHelpText` | `string` | `undefined` | Help text displayed in the answers section | +| `addAnswerText` | `string` | `undefined` | Custom text for the add-option button | +| `createPollButtonText` | `string` | `undefined` | Custom text for the create poll button | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `closeClick` | `void` | Emitted when the close button is clicked or Escape is pressed | +| `pollCreated` | `void` | Emitted when the poll is created successfully | +| `error` | `CometChat.CometChatException` | Emitted when an error occurs during poll creation | + +## Customization + +### CSS Variables + +The Create Poll component uses BEM-style CSS classes with CSS variable overrides: + +```css expandable +/* Modal overlay */ +.cometchat-create-poll { + background: var(--cometchat-background-color-01); +} + +/* Header */ +.cometchat-create-poll__header { + padding: var(--cometchat-spacing-3) var(--cometchat-spacing-4); + border-bottom: 1px solid var(--cometchat-border-color-light); +} + +/* Title */ +.cometchat-create-poll__title { + font: var(--cometchat-font-heading4-bold); + color: var(--cometchat-text-color-primary); +} + +/* Question input */ +.cometchat-create-poll__question-input { + font: var(--cometchat-font-body-regular); + color: var(--cometchat-text-color-primary); + border: 1px solid var(--cometchat-border-color-light); + border-radius: var(--cometchat-radius-2); +} + +/* Option input */ +.cometchat-create-poll__option-input { + font: var(--cometchat-font-body-regular); + color: var(--cometchat-text-color-primary); + border: 1px solid var(--cometchat-border-color-light); + border-radius: var(--cometchat-radius-2); +} + +/* Error message */ +.cometchat-create-poll__error { + font: var(--cometchat-font-caption1-regular); + color: var(--cometchat-error-color); +} +``` + +## Accessibility + +### Keyboard Navigation + +- **Tab / Shift+Tab**: Cycles focus through all interactive elements within the modal (focus trap) +- **Escape**: Closes the modal and restores focus to the previously focused element +- **Enter**: Submits the form when the create button is focused + +### Focus Management + +- Focus is trapped within the modal while it is open +- Initial focus is set to the close button on open +- When an option is added, focus moves to the new input field +- When an option is removed, focus moves to the nearest remaining option input +- On close, focus is restored to the element that was focused before the modal opened + +### Screen Reader Support + +- The modal uses `role="dialog"` with `aria-modal="true"` +- Form fields include associated labels for screen reader context + +## Related Components + +- [CometChatMessageComposer](/ui-kit/angular/components/cometchat-message-composer) - Parent component that triggers the poll creation dialog +- [CometChatPollBubble](/ui-kit/angular/components/cometchat-poll-bubble) - Renders poll messages in the message list diff --git a/ui-kit/angular/v5/components/cometchat-delete-bubble.mdx b/ui-kit/angular/components/cometchat-delete-bubble.mdx similarity index 100% rename from ui-kit/angular/v5/components/cometchat-delete-bubble.mdx rename to ui-kit/angular/components/cometchat-delete-bubble.mdx diff --git a/ui-kit/angular/components/cometchat-dropdown.mdx b/ui-kit/angular/components/cometchat-dropdown.mdx new file mode 100644 index 000000000..01c881fd2 --- /dev/null +++ b/ui-kit/angular/components/cometchat-dropdown.mdx @@ -0,0 +1,856 @@ +--- +title: DropDown +description: "A reusable dropdown component with keyboard navigation, outside click detection, and full accessibility support" +--- +The `CometChatDropDown` component provides a consistent, accessible dropdown implementation across the UIKit. It supports keyboard navigation, outside click detection, and full ARIA attributes for screen reader compatibility. + +## Overview + +The DropDown component is designed for selection patterns throughout your chat application: + +- **Keyboard Navigation**: Full support for Arrow keys, Enter, and Escape +- **Outside Click Detection**: Automatically closes when clicking outside +- **Accessibility**: Complete ARIA attributes and keyboard support +- **Customizable**: CSS variable-based styling for easy theming +- **Responsive**: Hover and focus states with visual feedback + +## Basic Usage + +### Simple Dropdown + +```html + + +``` + +### Dropdown with Selected Option + +```html + + +``` + +### Dropdown with Custom Placeholder + +```html + + +``` + + + **Live Preview** — interact with the default dropdown. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/base-elements-dropdown--default) + + + + +## Complete Example + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatDropDownComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-settings-panel', + standalone: true, + imports: [CometChatDropDownComponent], + template: ` +
+

Chat Settings

+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+
+ `, + styles: [` + .settings-panel { + padding: 20px; + max-width: 400px; + } + h3 { + margin: 0 0 20px 0; + font-size: 18px; + font-weight: 600; + } + .setting-item { + margin-bottom: 16px; + } + .setting-item label { + display: block; + margin-bottom: 8px; + font-size: 14px; + font-weight: 500; + } + `] +}) +export class SettingsPanelComponent { + themes = ['Light', 'Dark', 'Auto']; + selectedTheme = 'Light'; + + fontSizes = ['Small', 'Medium', 'Large']; + selectedFontSize = 'Medium'; + + languages = ['English', 'Spanish', 'French', 'German', 'Japanese']; + selectedLanguage = 'English'; + + onThemeChange(event: { value: string }): void { + this.selectedTheme = event.value; + console.log('Theme changed to:', event.value); + // Apply theme logic here + } + + onFontSizeChange(event: { value: string }): void { + this.selectedFontSize = event.value; + console.log('Font size changed to:', event.value); + // Apply font size logic here + } + + onLanguageChange(event: { value: string }): void { + this.selectedLanguage = event.value; + console.log('Language changed to:', event.value); + // Apply language logic here + } +} +``` + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `options` | `string[]` | `[]` | Array of options to display in the dropdown | +| `selectedOption` | `string` | `''` | Currently selected option. If empty, first option is selected by default | +| `placeholder` | `string` | `'Select an option'` | Placeholder text shown when no option is selected | +| `ariaLabel` | `string` | `undefined` | Custom ARIA label for the dropdown button. If not provided, uses the display value | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `optionsChanged` | `EventEmitter<{ value: string }>` | Emitted when an option is selected. Provides the selected option value | + +## Behavior + +### Option Selection + +The dropdown can be controlled or uncontrolled: + +```typescript expandable +// Controlled dropdown +@Component({ + template: ` + + + ` +}) +export class ControlledDropdownComponent { + options = ['Option 1', 'Option 2', 'Option 3']; + selectedValue = 'Option 1'; + + onOptionChange(event: { value: string }): void { + this.selectedValue = event.value; + } +} +``` + +### Default Selection + +If no `selectedOption` is provided, the first option in the array is selected by default: + +```typescript expandable +@Component({ + template: ` + + + + ` +}) +export class DefaultSelectionComponent { + onOptionChange(event: { value: string }): void { + console.log('Selected:', event.value); + } +} +``` + +### Outside Click Detection + +The dropdown automatically closes when clicking outside the component: + +```typescript +// No additional configuration needed + + +``` + +## Customization + +### Styling with CSS Variables + +The DropDown component uses CSS variables for comprehensive theming: + +```css expandable +/* Custom dropdown styling */ +cometchat-dropdown { + --cometchat-background-color: #ffffff; + --cometchat-border-color: #e0e0e0; + --cometchat-primary-color: #3399ff; + --cometchat-text-color: #000000; + --cometchat-hover-background-color: #f5f5f5; + --cometchat-selected-background-color: #e6f2ff; + --cometchat-font-family: 'Inter', sans-serif; + --cometchat-font-size-medium: 14px; + --cometchat-border-radius: 4px; + --cometchat-spacing-2: 8px; + --cometchat-spacing-3: 12px; + --cometchat-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); +} +``` + +### Available CSS Variables + +| Variable | Purpose | Default | +|----------|---------|---------| +| `--cometchat-background-color` | Background color of button and menu | `#ffffff` | +| `--cometchat-border-color` | Border color | `#e0e0e0` | +| `--cometchat-primary-color` | Focus outline and selected text color | `#3399ff` | +| `--cometchat-text-color` | Text color | `#000000` | +| `--cometchat-hover-background-color` | Option hover background | `#f5f5f5` | +| `--cometchat-selected-background-color` | Selected option background | `#e6f2ff` | +| `--cometchat-font-family` | Font family | `inherit` | +| `--cometchat-font-size-medium` | Font size | `14px` | +| `--cometchat-border-radius` | Border radius | `4px` | +| `--cometchat-spacing-2` | Small spacing | `8px` | +| `--cometchat-spacing-3` | Medium spacing | `12px` | +| `--cometchat-box-shadow` | Dropdown menu shadow | `0 2px 8px rgba(0, 0, 0, 0.15)` | + +### Custom Color Schemes + +```css expandable +/* Dark theme dropdown */ +.dropdown-dark cometchat-dropdown { + --cometchat-background-color: #2c2c2c; + --cometchat-border-color: #444444; + --cometchat-text-color: #ffffff; + --cometchat-hover-background-color: #3a3a3a; + --cometchat-selected-background-color: #1a4d7a; +} + +/* Success dropdown */ +.dropdown-success cometchat-dropdown { + --cometchat-primary-color: #4CAF50; + --cometchat-selected-background-color: #e8f5e9; +} + +/* Warning dropdown */ +.dropdown-warning cometchat-dropdown { + --cometchat-primary-color: #FF9800; + --cometchat-selected-background-color: #fff3e0; +} +``` + +### Custom Sizes + +```css expandable +/* Large dropdown */ +.dropdown-large cometchat-dropdown { + --cometchat-font-size-medium: 16px; + --cometchat-spacing-2: 12px; + --cometchat-spacing-3: 16px; +} + +/* Small dropdown */ +.dropdown-small cometchat-dropdown { + --cometchat-font-size-medium: 12px; + --cometchat-spacing-2: 6px; + --cometchat-spacing-3: 8px; +} + +/* Compact dropdown */ +.dropdown-compact .cometchat-dropdown__button { + padding: 4px 8px; +} +``` + + + **Live Preview** — dropdown with categorized options. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/base-elements-dropdown--with-options) + + + + +## Accessibility + +The DropDown component includes comprehensive accessibility features following WCAG 2.1 Level AA guidelines: + +### Keyboard Support + +The dropdown provides full keyboard navigation support: + +| Key | Action | +|-----|--------| +| `Tab` | Moves focus to/from the dropdown button | +| `Shift + Tab` | Moves focus backward to previous element | +| `Enter` or `Space` | Opens the dropdown (when closed) or selects the focused option (when open) | +| `Escape` | Closes the dropdown and restores focus to the trigger button | +| `ArrowDown` | Moves focus to the next option in the list (wraps to first when at end) | +| `ArrowUp` | Moves focus to the previous option in the list (wraps to last when at beginning) | +| `Home` | Moves focus to the first option in the list | +| `End` | Moves focus to the last option in the list | +| `Any letter/number` | Type-ahead search: jumps to the first option starting with the typed character(s) | + +### Type-Ahead Search + +The dropdown includes intelligent type-ahead search functionality: + +- **Single Character**: Type any letter to jump to the first option starting with that letter +- **Multiple Characters**: Continue typing within 500ms to refine the search (e.g., type "ap" to find "Apple") +- **Case Insensitive**: Works regardless of letter case +- **Auto-Clear**: The search buffer clears after 500ms of no typing + +```typescript +// Example: Options list +options = ['Apple', 'Apricot', 'Banana', 'Cherry', 'Date']; + +// User types 'a' → focuses 'Apple' +// User types 'p' (within 500ms) → focuses 'Apple' (starts with 'ap') +// User types 'r' (within 500ms) → focuses 'Apricot' (starts with 'apr') +``` + +### Focus Management + +The dropdown implements proper focus management: + +- **Focus Trap**: When open, keyboard navigation is contained within the dropdown +- **Focus Restoration**: When closed (via Escape, selection, or outside click), focus returns to the trigger button +- **Initial Focus**: When opened, the currently selected option is focused (or first option if none selected) + +### ARIA Attributes + +The component uses proper ARIA attributes for screen reader compatibility: + +| Attribute | Element | Purpose | +|-----------|---------|---------| +| `role="button"` | Trigger button | Identifies the button as an interactive element | +| `aria-expanded` | Trigger button | Indicates whether the dropdown is open (`"true"`) or closed (`"false"`) | +| `aria-haspopup="menu"` | Trigger button | Indicates the button has a popup menu | +| `aria-controls` | Trigger button | References the ID of the dropdown menu | +| `aria-label` | Trigger button | Provides accessible label (uses `ariaLabel` input or display value) | +| `role="menu"` | Dropdown menu | Identifies the dropdown as a menu | +| `aria-label` | Dropdown menu | Provides accessible label for the menu | +| `role="menuitem"` | Menu options | Identifies each option as a menu item | +| `aria-selected` | Menu options | Indicates which option is currently selected (`"true"` or `"false"`) | +| `aria-hidden="true"` | Icon | Hides decorative icon from screen readers | + +### Custom ARIA Labels + +You can provide custom ARIA labels for better screen reader experience: + +```html + + +``` + +### Focus Indicators + +The dropdown provides clear visual focus indicators that meet WCAG contrast requirements: + +- **Button Focus**: 2px solid outline in primary color with 2px offset +- **Option Focus**: Background highlight on focused option +- **Keyboard-Only**: Uses `:focus-visible` to show focus indicators only during keyboard navigation + +```css +/* Focus styling is built-in and WCAG compliant */ +.cometchat-dropdown__button:focus-visible { + outline: 2px solid var(--cometchat-primary-color, #3399ff); + outline-offset: 2px; +} + +.cometchat-dropdown__option--focused { + background-color: var(--cometchat-hover-background-color, #f5f5f5); +} +``` + +### Screen Reader Announcements + +The dropdown provides appropriate announcements for screen reader users: + +- **Button State**: Announces "collapsed" or "expanded" based on dropdown state +- **Selected Option**: Announces the currently selected option +- **Option Count**: Screen readers can navigate through options and hear "X of Y" +- **Selection Change**: Announces when a new option is selected + +## Examples + +### User Role Selector + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatDropDownComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-role-selector', + standalone: true, + imports: [CometChatDropDownComponent], + template: ` +
+

Select User Role

+ + +

{{ getRoleDescription() }}

+
+ `, + styles: [` + .role-selector { + padding: 20px; + max-width: 300px; + } + h4 { + margin: 0 0 12px 0; + font-size: 16px; + } + .role-description { + margin-top: 12px; + font-size: 13px; + color: #666; + } + `] +}) +export class RoleSelectorComponent { + roles = ['Admin', 'Moderator', 'Member', 'Guest']; + selectedRole = 'Member'; + + roleDescriptions: { [key: string]: string } = { + 'Admin': 'Full access to all features and settings', + 'Moderator': 'Can manage users and content', + 'Member': 'Standard user with basic permissions', + 'Guest': 'Limited access, read-only mode' + }; + + onRoleChange(event: { value: string }): void { + this.selectedRole = event.value; + console.log('Role changed to:', event.value); + } + + getRoleDescription(): string { + return this.roleDescriptions[this.selectedRole] || ''; + } +} +``` + +### Filter Dropdown + +```typescript expandable +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CometChatDropDownComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-message-filter', + standalone: true, + imports: [CommonModule, CometChatDropDownComponent], + template: ` +
+
+

Messages

+ + +
+ +
+ @for (message of filteredMessages; track message.id) { +
+ {{ message.text }} +
+ } +
+
+ `, + styles: [` + .message-filter { + padding: 20px; + } + .filter-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + } + .filter-header h3 { + margin: 0; + } + .filter-header cometchat-dropdown { + width: 150px; + } + .message-list { + display: flex; + flex-direction: column; + gap: 12px; + } + .message-item { + padding: 12px; + background: #f5f5f5; + border-radius: 8px; + } + `] +}) +export class MessageFilterComponent { + filterOptions = ['All', 'Unread', 'Starred', 'Archived']; + selectedFilter = 'All'; + + messages = [ + { text: 'Hello there!', read: true, starred: false, archived: false }, + { text: 'How are you?', read: false, starred: true, archived: false }, + { text: 'Meeting at 3pm', read: false, starred: false, archived: false }, + { text: 'Old message', read: true, starred: false, archived: true } + ]; + + get filteredMessages() { + switch (this.selectedFilter) { + case 'Unread': + return this.messages.filter(m => !m.read); + case 'Starred': + return this.messages.filter(m => m.starred); + case 'Archived': + return this.messages.filter(m => m.archived); + default: + return this.messages.filter(m => !m.archived); + } + } + + onFilterChange(event: { value: string }): void { + this.selectedFilter = event.value; + } +} +``` + +### Sort Dropdown + +```typescript expandable +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CometChatDropDownComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-conversation-list', + standalone: true, + imports: [CommonModule, CometChatDropDownComponent], + template: ` +
+
+

Conversations

+
+ Sort by: + + +
+
+ +
+ @for (conv of sortedConversations; track conv.id) { +
+ {{ conv.name }} + {{ conv.timestamp }} +
+ } +
+
+ `, + styles: [` + .conversation-list { + padding: 20px; + } + .list-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + } + .list-header h3 { + margin: 0; + } + .sort-control { + display: flex; + align-items: center; + gap: 8px; + } + .sort-control span { + font-size: 14px; + color: #666; + } + .sort-control cometchat-dropdown { + width: 150px; + } + .conversations { + display: flex; + flex-direction: column; + gap: 12px; + } + .conversation-item { + display: flex; + justify-content: space-between; + padding: 12px; + background: #f9f9f9; + border-radius: 8px; + } + .timestamp { + font-size: 12px; + color: #999; + } + `] +}) +export class ConversationListComponent { + sortOptions = ['Recent', 'Name (A-Z)', 'Name (Z-A)', 'Unread First']; + selectedSort = 'Recent'; + + conversations = [ + { name: 'Alice', timestamp: '2 min ago', unread: 3, time: Date.now() - 120000 }, + { name: 'Bob', timestamp: '1 hour ago', unread: 0, time: Date.now() - 3600000 }, + { name: 'Charlie', timestamp: '5 min ago', unread: 1, time: Date.now() - 300000 } + ]; + + get sortedConversations() { + const sorted = [...this.conversations]; + + switch (this.selectedSort) { + case 'Recent': + return sorted.sort((a, b) => b.time - a.time); + case 'Name (A-Z)': + return sorted.sort((a, b) => a.name.localeCompare(b.name)); + case 'Name (Z-A)': + return sorted.sort((a, b) => b.name.localeCompare(a.name)); + case 'Unread First': + return sorted.sort((a, b) => b.unread - a.unread); + default: + return sorted; + } + } + + onSortChange(event: { value: string }): void { + this.selectedSort = event.value; + } +} +``` + +## Best Practices + + + Always provide meaningful option labels that clearly describe what each option does. + + + + The dropdown automatically closes when an option is selected or when clicking outside. You don't need to manage the open/close state manually. + + + + The first option is selected by default if no `selectedOption` is provided. Make sure your first option is a sensible default. + + + + Use keyboard navigation for better accessibility. The dropdown fully supports Arrow keys, Enter, Space, Escape, Home, End, and type-ahead search. + + + + Provide custom `ariaLabel` values for dropdowns that need more context than the selected value provides (e.g., "Theme selector" instead of just "Light"). + + +### Accessibility Best Practices + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatDropDownComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-accessible-settings', + standalone: true, + imports: [CometChatDropDownComponent], + template: ` +
+

Preferences

+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+
+ `, + styles: [` + .settings-panel { + padding: var(--cometchat-spacing-4); + max-width: 400px; + } + h3 { + margin: 0 0 var(--cometchat-spacing-4) 0; + font: var(--cometchat-font-heading3-bold); + } + .setting-item { + margin-bottom: var(--cometchat-spacing-3); + } + .setting-item label { + display: block; + margin-bottom: var(--cometchat-spacing-2); + font: var(--cometchat-font-body-medium); + } + `] +}) +export class AccessibleSettingsComponent { + themes = ['Light', 'Dark', 'Auto']; + selectedTheme = 'Light'; + + fontSizes = ['Small (12px)', 'Medium (14px)', 'Large (16px)', 'Extra Large (18px)']; + selectedFontSize = 'Medium (14px)'; + + languages = ['English', 'Spanish', 'French', 'German', 'Japanese', 'Chinese']; + selectedLanguage = 'English'; + + onThemeChange(event: { value: string }): void { + this.selectedTheme = event.value; + // Announce change to screen readers + this.announceChange(`Theme changed to ${event.value}`); + } + + onFontSizeChange(event: { value: string }): void { + this.selectedFontSize = event.value; + this.announceChange(`Font size changed to ${event.value}`); + } + + onLanguageChange(event: { value: string }): void { + this.selectedLanguage = event.value; + this.announceChange(`Language changed to ${event.value}`); + } + + private announceChange(message: string): void { + // Create a live region for screen reader announcements + const announcement = document.createElement('div'); + announcement.setAttribute('role', 'status'); + announcement.setAttribute('aria-live', 'polite'); + announcement.className = 'sr-only'; + announcement.textContent = message; + document.body.appendChild(announcement); + + // Remove after announcement + setTimeout(() => { + document.body.removeChild(announcement); + }, 1000); + } +} +``` + +## Related Components + +- **CometChatCheckbox**: For multiple selections +- **CometChatRadioButton**: For mutually exclusive selections without a dropdown +- **CometChatSearchBar**: For searchable selection lists + +## Technical Details + +- **Standalone Component**: Can be imported and used independently +- **Change Detection**: Uses default change detection strategy +- **Dependencies**: Only requires Angular CommonModule +- **Bundle Size**: Minimal footprint (~4KB with type-ahead search) +- **BEM CSS**: Follows Block Element Modifier naming convention +- **Accessibility**: WCAG 2.1 Level AA compliant + - Full keyboard navigation support + - Proper ARIA attributes and roles + - Focus management and restoration + - Type-ahead search functionality + - Screen reader compatible +- **Outside Click Detection**: Uses HostListener for efficient event handling +- **Focus Indicators**: Uses `:focus-visible` for keyboard-only focus indicators + + + diff --git a/ui-kit/angular/components/cometchat-error-boundary.mdx b/ui-kit/angular/components/cometchat-error-boundary.mdx new file mode 100644 index 000000000..6ef062f86 --- /dev/null +++ b/ui-kit/angular/components/cometchat-error-boundary.mdx @@ -0,0 +1,133 @@ +--- +title: "CometChatErrorBoundary" +description: "A reusable wrapper component that catches errors from projected child content and renders a fallback UI" +--- +The `CometChatErrorBoundary` component wraps child content via `ng-content` and renders a fallback UI when an error is reported. It supports custom fallback templates and emits structured error context for logging and analytics. + +## Overview + +- **Error Isolation**: Prevents a rendering error in one component from crashing the entire view +- **Custom Fallback**: Accepts an optional `TemplateRef` for custom error UI +- **Signal-Based State**: Uses Angular signals for efficient error state management +- **Retry Support**: Built-in retry button to reset error state and re-render children +- **Structured Error Context**: Emits `ErrorContext` with error, component name, and timestamp +- **OnPush Change Detection**: Efficient rendering with `ChangeDetectionStrategy.OnPush` + + + **Live Preview** — default error boundary preview. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-error-boundary--default) + + + + +## Basic Usage + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatErrorBoundaryComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-safe-wrapper', + standalone: true, + imports: [CometChatErrorBoundaryComponent], + template: ` + + + + ` +}) +export class SafeWrapperComponent { + msg: any; + + onError(ctx: any): void { + console.error('Error in', ctx.componentName, ctx.error); + } +} +``` + +### With Custom Fallback Template + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatErrorBoundaryComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-custom-fallback', + standalone: true, + imports: [CometChatErrorBoundaryComponent], + template: ` + + + + + +
+

Error in {{ ctx.componentName }}: {{ ctx.error.message }}

+ {{ ctx.timestamp | date:'medium' }} +
+
+ ` +}) +export class CustomFallbackComponent {} +``` + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `fallbackView` | `TemplateRef<{ $implicit: ErrorContext }>` | `undefined` | Optional custom fallback template rendered when an error occurs. Receives `ErrorContext` as implicit context. | +| `componentName` | `string` | `'Unknown'` | Name identifying the wrapped component, used in the emitted `ErrorContext`. | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `error` | `ErrorContext` | Emitted when an error is caught. Contains `error`, `componentName`, and `timestamp`. | + +### ErrorContext Interface + +```typescript +interface ErrorContext { + error: Error; // The original error that was caught + componentName: string; // Name identifying the source component + timestamp: number; // Epoch milliseconds when the error occurred +} +``` + +## Customization + +### Styling with CSS Variables + +The default fallback UI uses BEM-named CSS classes prefixed with `cometchat-error-boundary`: + +```css +cometchat-error-boundary { + --cometchat-background-color-01: #ffffff; + --cometchat-text-color-primary: #141414; + --cometchat-text-color-secondary: #727272; + --cometchat-primary-color: #6852D6; + --cometchat-spacing-3: 12px; + --cometchat-spacing-4: 16px; +} +``` + +### Dark Theme + +```css +[data-theme="dark"] cometchat-error-boundary { + --cometchat-background-color-01: #1a1a1a; + --cometchat-text-color-primary: #ffffff; + --cometchat-text-color-secondary: #cccccc; +} +``` diff --git a/ui-kit/angular/v5/components/cometchat-file-bubble.mdx b/ui-kit/angular/components/cometchat-file-bubble.mdx similarity index 99% rename from ui-kit/angular/v5/components/cometchat-file-bubble.mdx rename to ui-kit/angular/components/cometchat-file-bubble.mdx index ad730fd8a..ea6881cee 100644 --- a/ui-kit/angular/v5/components/cometchat-file-bubble.mdx +++ b/ui-kit/angular/components/cometchat-file-bubble.mdx @@ -499,5 +499,5 @@ Screen readers announce the file bubble with: -For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/v5/troubleshooting#cometchatfilebubble). +For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchatfilebubble). diff --git a/ui-kit/angular/components/cometchat-flag-message-dialog.mdx b/ui-kit/angular/components/cometchat-flag-message-dialog.mdx new file mode 100644 index 000000000..15f0f3934 --- /dev/null +++ b/ui-kit/angular/components/cometchat-flag-message-dialog.mdx @@ -0,0 +1,148 @@ +--- +title: "CometChatFlagMessageDialog" +description: "A dialog component for flagging/reporting inappropriate messages with reason selection and optional remark" +--- + +## Overview + +The CometChatFlagMessageDialog component provides a modal dialog for users to report inappropriate messages. It fetches available flag reasons from the CometChat SDK, allows the user to select a reason, and optionally add a remark before submitting. + +### Key Features + +- **SDK-Driven Reasons**: Fetches flag reasons dynamically from `CometChat.getFlagReasons()` +- **Optional Remark**: Configurable text area for additional context (max 500 characters) +- **Focus Trapping**: Full focus trap within the dialog for accessibility +- **Keyboard Navigation**: Escape to close, Tab to navigate, Enter/Space to select +- **Screen Reader Support**: ARIA roles, labels, and live announcements +- **Loading State**: Visual feedback during submission + +## Basic Usage + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatFlagMessageDialogComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-flag-dialog', + standalone: true, + imports: [CometChatFlagMessageDialogComponent], + template: ` + @if (showDialog && messageToFlag) { + + + } + ` +}) +export class FlagDialogComponent { + showDialog = false; + messageToFlag: CometChat.BaseMessage | null = null; + + openDialog(message: CometChat.BaseMessage): void { + this.messageToFlag = message; + this.showDialog = true; + } + + onConfirm(event: { message: CometChat.BaseMessage; reasonId: string; remark: string }): void { + CometChat.flagMessage(event.message.getId(), event.reasonId, event.remark).then( + () => { this.showDialog = false; }, + (error) => { console.error('Flag failed:', error); } + ); + } + + onCancel(): void { + this.showDialog = false; + } +} +``` + +### Hiding the Remark Field + +```typescript + + +``` + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `message` | `CometChat.BaseMessage` | required | The message being flagged/reported. | +| `hideRemarkField` | `boolean` | `false` | When `true`, hides the optional remark text area. | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `confirm` | `{ message: CometChat.BaseMessage; reasonId: string; remark: string }` | Emitted when the user confirms the flag. Contains the message, selected reason ID, and optional remark text. | +| `cancel` | `void` | Emitted when the user cancels the dialog (via Cancel button or Escape key). | + +## Public Methods + +These methods can be called on the component instance (via `@ViewChild`): + +| Method | Description | +|--------|-------------| +| `setSuccess()` | Call after a successful flag API call to reset loading/error states. | +| `setError()` | Call after a failed flag API call to show the error state. | + +## Keyboard Accessibility + +| Key | Action | +|-----|--------| +| `Tab` | Navigate between focusable elements within the dialog | +| `Shift + Tab` | Navigate backwards | +| `Escape` | Close the dialog (emits `cancel`) | +| `Enter` / `Space` | Select a reason or activate a button | + +### Accessibility Features + +- `role="dialog"` with `aria-modal="true"` on the container +- `aria-labelledby` pointing to the dialog title +- `aria-describedby` pointing to the dialog description +- Focus is trapped within the dialog while open +- Focus is restored to the previously focused element on close +- Character limit reached is announced via `aria-live` region + +## Localization Keys + +| Key | Default (en-us) | +|-----|-----------------| +| `flag_message_title` | "Report Message" | +| `flag_message_subtitle` | "Why are you reporting this message?" | +| `flag_message_remark_label` | "Additional details" | +| `flag_message_remark_optional` | "(optional)" | +| `flag_message_remark_placeholder` | "Add more context..." | +| `flag_message_confirm_yes` | "Report" | +| `flag_message_confirm_no` | "Cancel" | +| `flag_message_error` | "Something went wrong. Please try again." | +| `flag_message_character_limit_reached` | "Character limit reached" | +| `flag_message_reason_id_{id}` | Falls back to reason name from SDK | + +## CSS Variables + +```css +cometchat-flag-message-dialog { + --cometchat-background-color-01: #ffffff; + --cometchat-text-color-primary: #141414; + --cometchat-text-color-secondary: #727272; + --cometchat-border-color-light: #e8e8e8; + --cometchat-primary-color: #6852D6; + --cometchat-error-color: #F44336; + --cometchat-radius-2: 8px; + --cometchat-radius-3: 12px; +} +``` + +## Related Components + +- [CometChatMessageList](/ui-kit/angular/components/cometchat-message-list) — Triggers the flag dialog via message options +- [CometChatMessageBubble](/ui-kit/angular/components/cometchat-message-bubble) — Individual message that can be flagged +- [CometChatConfirmDialog](/ui-kit/angular/components/confirm-dialog) — Similar dialog pattern for confirmations diff --git a/ui-kit/angular/components/cometchat-fullscreen-viewer.mdx b/ui-kit/angular/components/cometchat-fullscreen-viewer.mdx new file mode 100644 index 000000000..d1623abfb --- /dev/null +++ b/ui-kit/angular/components/cometchat-fullscreen-viewer.mdx @@ -0,0 +1,1542 @@ +--- +title: "CometChatFullscreenViewer" +description: "A comprehensive Angular component for viewing images and videos in fullscreen mode with gallery navigation, keyboard controls, and accessibility support" +--- +The `CometChatFullscreenViewer` component is a sophisticated fullscreen media viewer that displays images and videos in an immersive modal experience. It serves as the unified viewer for all media types in the CometChat Angular UIKit, supporting both single media viewing and gallery-style navigation through multiple attachments. + +## Overview + +The Fullscreen Viewer component provides a rich media viewing experience while maintaining visual consistency with the design system and ensuring full accessibility: + +- **Unified Media Viewing**: Single component for both images and videos +- **Gallery Mode**: Navigate through multiple media attachments with previous/next controls +- **Single Mode**: Display individual media items (backward compatible) +- **Keyboard Navigation**: Full keyboard support (Arrow keys, Escape) +- **Sender Information**: Display sender avatar and name in header +- **Position Indicator**: Shows current position in gallery (e.g., "3 of 10") +- **Download Support**: Download button for saving media to device +- **Video Playback Control**: Automatic video stop on navigation and close +- **Body Scroll Management**: Prevents background scrolling when viewer is open +- **Focus Management**: Traps focus within viewer and restores on close +- **Accessibility**: Full WCAG 2.1 Level AA compliance with ARIA support + +## Basic Usage + +### Simple Image Viewer + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatFullScreenViewerComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-image-viewer', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` + + + + ` +}) +export class ImageViewerComponent { + isViewerOpen = false; + imageUrl = 'https://example.com/image.jpg'; + + openViewer(): void { + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } +} +``` + +### Simple Video Viewer + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatFullScreenViewerComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-video-viewer', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` + + + + ` +}) +export class VideoViewerComponent { + isViewerOpen = false; + videoUrl = 'https://example.com/video.mp4'; + + openViewer(): void { + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } +} +``` + +### Gallery Mode with Multiple Images + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatFullScreenViewerComponent, MediaAttachment } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-image-gallery', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` + + + + ` +}) +export class ImageGalleryComponent { + isViewerOpen = false; + + imageAttachments: MediaAttachment[] = [ + { url: 'https://example.com/image1.jpg', type: 'image', name: 'Photo 1.jpg' }, + { url: 'https://example.com/image2.jpg', type: 'image', name: 'Photo 2.jpg' }, + { url: 'https://example.com/image3.jpg', type: 'image', name: 'Photo 3.jpg' } + ]; + + openGallery(): void { + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } + + onIndexChange(index: number): void { + console.log('Now viewing image:', index + 1); + } +} +``` + + + **Live Preview** — interact with the default fullscreen viewer. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-fullscreen-viewer--default) + + + + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `isOpen` | `boolean` | `false` | Controls the visibility of the fullscreen viewer | +| `attachments` | `MediaAttachment[]` | `[]` | Array of media attachments for gallery mode | +| `startIndex` | `number` | `0` | Starting index when opening gallery mode | +| `url` | `string` | `''` | Single media URL for single mode (backward compatible) | +| `mediaType` | `'image' \| 'video' \| 'audio' \| 'file'` | `'image'` | Type of media for single mode | +| `message` | `CometChat.BaseMessage` | `undefined` | CometChat message object for extracting sender info (optional) | +| `explicitSenderName` | `string` | `''` | Explicit sender name to display in header | +| `explicitSenderAvatarUrl` | `string` | `''` | Explicit sender avatar URL to display in header | +| `fileName` | `string` | `''` | File name for file preview display | +| `fileSize` | `number` | `undefined` | File size in bytes for file preview display | +| `placeholderImage` | `string` | `undefined` | Placeholder image URL shown while media is loading | +| `imageSentAtDateTimeFormat` | `CalendarObject` | `undefined` | Custom format for timestamps associated with images | +| `downloadIconUrl` | `string` | `'assets/download.svg'` | Custom download button icon URL | +| `prevIconUrl` | `string` | `'assets/arrow_back.svg'` | Custom previous button icon URL | +| `nextIconUrl` | `string` | `'assets/arrow_forward.svg'` | Custom next button icon URL | +| `enablePictureInPicture` | `boolean` | `true` | Enable/disable Picture-in-Picture mode for videos | +| `pipIconUrl` | `string` | `'assets/pip.svg'` | Custom PIP button icon URL | +| `pipExitIconUrl` | `string` | `'assets/pip_exit.svg'` | Custom PIP exit button icon URL | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `closeClick` | `void` | Emitted when the close button is clicked or Escape key is pressed | +| `downloadClick` | `MediaAttachment \| string` | Emitted when the download button is clicked. Contains the current media attachment or URL | +| `indexChange` | `number` | Emitted when navigating between media items in gallery mode. Contains the new index | +| `pipStateChange` | `boolean` | Emitted when entering or exiting Picture-in-Picture mode. `true` when entering, `false` when exiting | + +## MediaAttachment Interface + +```typescript +interface MediaAttachment { + url: string; // Required: Media URL + type: 'image' | 'video'; // Required: Media type + name?: string; // Optional: Filename for download + mimeType?: string; // Optional: MIME type + size?: number; // Optional: File size in bytes +} +``` + +## Advanced Usage + +### Gallery Mode with Mixed Media + +The viewer seamlessly handles both images and videos in the same gallery: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatFullScreenViewerComponent, MediaAttachment } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-mixed-gallery', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` + + + + ` +}) +export class MixedGalleryComponent { + isViewerOpen = false; + currentIndex = 0; + + mixedAttachments: MediaAttachment[] = [ + { url: 'https://example.com/photo1.jpg', type: 'image', name: 'Vacation Photo.jpg' }, + { url: 'https://example.com/video1.mp4', type: 'video', name: 'Beach Video.mp4' }, + { url: 'https://example.com/photo2.jpg', type: 'image', name: 'Sunset.jpg' }, + { url: 'https://example.com/video2.mp4', type: 'video', name: 'Highlights.mp4' } + ]; + + openGallery(): void { + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } + + onIndexChange(index: number): void { + this.currentIndex = index; + console.log('Now viewing:', this.mixedAttachments[index].name); + } + + onDownload(media: MediaAttachment | string): void { + console.log('Downloading:', media); + } +} +``` + +**Mixed Gallery Features:** +- Seamlessly switches between image and video rendering +- Videos automatically stop when navigating away +- Each media type displays appropriately (img element for images, video element with controls for videos) +- Download works for both images and videos + + + **Live Preview** — fullscreen viewer with a portrait-oriented image. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-fullscreen-viewer--single-image) + + + + +### Opening Gallery at Specific Index + +Start the gallery at any position using the `startIndex` property: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatFullScreenViewerComponent, MediaAttachment } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-thumbnail-grid', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` +
+ @for (attachment of attachments; track attachment.url; let i = $index) { + + } +
+ + + `, + styles: [` + .thumbnail-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 12px; + } + + .thumbnail { + width: 100%; + height: 150px; + object-fit: cover; + border-radius: 8px; + cursor: pointer; + transition: transform 0.2s; + } + + .thumbnail:hover { + transform: scale(1.05); + } + `] +}) +export class ThumbnailGridComponent { + isViewerOpen = false; + selectedIndex = 0; + + attachments: MediaAttachment[] = [ + { url: 'https://example.com/img1.jpg', type: 'image' }, + { url: 'https://example.com/img2.jpg', type: 'image' }, + { url: 'https://example.com/img3.jpg', type: 'image' }, + { url: 'https://example.com/img4.jpg', type: 'image' } + ]; + + openGalleryAt(index: number): void { + this.selectedIndex = index; + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } +} +``` + +### Using with CometChat Messages + +Extract attachments from CometChat.MediaMessage objects: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { CometChatFullScreenViewerComponent, MediaAttachment } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-message-viewer', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` + + ` +}) +export class MessageViewerComponent { + isViewerOpen = false; + message!: CometChat.MediaMessage; + mediaAttachments: MediaAttachment[] = []; + + openMessageMedia(message: CometChat.MediaMessage): void { + this.message = message; + + // Extract attachments from message + const attachments = message.getAttachments(); + this.mediaAttachments = attachments.map(att => ({ + url: att.url, + type: message.getType() === 'image' ? 'image' : 'video', + name: att.name, + mimeType: att.mimeType, + size: att.size + })); + + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } +} +``` + +### Keyboard Navigation + +The viewer provides full keyboard support for navigation and control: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatFullScreenViewerComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-keyboard-demo', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` +
+

Keyboard Shortcuts:

+
    +
  • Next image/video
  • +
  • Previous image/video
  • +
  • Esc Close viewer
  • +
  • Tab Navigate controls
  • +
  • Enter Activate focused button
  • +
+ +
+ + + `, + styles: [` + .instructions { + padding: 20px; + background: #f5f5f5; + border-radius: 8px; + } + + kbd { + padding: 2px 6px; + background: #fff; + border: 1px solid #ccc; + border-radius: 4px; + font-family: monospace; + } + `] +}) +export class KeyboardDemoComponent { + isViewerOpen = false; + attachments = [ + { url: 'https://example.com/img1.jpg', type: 'image' as const }, + { url: 'https://example.com/img2.jpg', type: 'image' as const }, + { url: 'https://example.com/img3.jpg', type: 'image' as const } + ]; + + openViewer(): void { + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } +} +``` + +**Keyboard Shortcuts:** +- **Right Arrow (→)**: Navigate to next media item +- **Left Arrow (←)**: Navigate to previous media item +- **Escape (Esc)**: Close the viewer +- **Tab**: Navigate between interactive elements (close, download, previous, next buttons) +- **Enter/Space**: Activate the focused button + +### Video Playback Management + +The viewer automatically manages video playback to prevent resource leaks: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatFullScreenViewerComponent, MediaAttachment } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-video-gallery', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` + + + + ` +}) +export class VideoGalleryComponent { + isViewerOpen = false; + + videoAttachments: MediaAttachment[] = [ + { url: 'https://example.com/video1.mp4', type: 'video', name: 'Tutorial Part 1.mp4' }, + { url: 'https://example.com/video2.mp4', type: 'video', name: 'Tutorial Part 2.mp4' }, + { url: 'https://example.com/video3.mp4', type: 'video', name: 'Tutorial Part 3.mp4' } + ]; + + openViewer(): void { + this.isViewerOpen = true; + } + + onNavigate(index: number): void { + console.log('Navigated to video:', index + 1); + // Previous video automatically stopped + } + + onClose(): void { + console.log('Viewer closed'); + // All videos automatically stopped + this.isViewerOpen = false; + } +} +``` + +**Video Playback Features:** +- Videos display with standard HTML5 controls (play, pause, seek, volume, fullscreen) +- Automatic video stop when navigating to another media item +- Automatic video stop when closing the viewer +- Automatic video stop on component destruction +- Prevents memory leaks and improves performance + +## Customization + +### Styling with CSS Variables + +The Fullscreen Viewer component uses CSS variables exclusively for easy customization: + +```css expandable +/* Custom fullscreen viewer styling */ +cometchat-fullscreen-viewer { + /* Spacing */ + --cometchat-spacing-2: 8px; + --cometchat-spacing-3: 12px; + --cometchat-spacing-4: 16px; + --cometchat-spacing-6: 24px; + + /* Typography */ + --cometchat-font-body-medium: 500 14px 'Inter'; + --cometchat-font-caption1-regular: 400 12px 'Inter'; + + /* Colors */ + --cometchat-background-color-01: #FFFFFF; + --cometchat-text-color-primary: #141414; + --cometchat-text-color-white: #FFFFFF; + --cometchat-icon-color-white: #FFFFFF; + + /* Border radius */ + --cometchat-radius-2: 8px; + --cometchat-radius-max: 1000px; +} +``` + +### Available CSS Variables + +| Variable | Purpose | Default | +|----------|---------|---------| +| `--cometchat-spacing-2` to `--cometchat-spacing-6` | Padding, margin, gap | `8px` to `24px` | +| `--cometchat-font-body-medium` | Sender name font | `500 14px Roboto` | +| `--cometchat-font-caption1-regular` | Index display font | `400 12px Roboto` | +| `--cometchat-background-color-01` | Header background | `#FFFFFF` | +| `--cometchat-text-color-primary` | Sender name color | `#141414` | +| `--cometchat-text-color-white` | Index text color | `#FFFFFF` | +| `--cometchat-icon-color-white` | Button icon color | `#FFFFFF` | +| `--cometchat-radius-2` | Avatar border radius | `8px` | +| `--cometchat-radius-max` | Button border radius | `1000px` (circular) | + +### Custom Color Schemes + +```css expandable +/* Dark theme */ +[data-theme="dark"] cometchat-fullscreen-viewer { + --cometchat-background-color-01: rgba(30, 30, 30, 0.95); + --cometchat-text-color-primary: #FFFFFF; +} + +/* Custom accent color */ +.custom-viewer cometchat-fullscreen-viewer { + --cometchat-primary-color: #FF5722; +} +``` + +### Custom Button Styling + +```css expandable +/* Custom navigation buttons */ +::ng-deep .cometchat-fullscreen-viewer__nav-button { + background: rgba(255, 255, 255, 0.2); + border-radius: 50%; + width: 48px; + height: 48px; + backdrop-filter: blur(10px); + transition: all 0.2s; +} + +::ng-deep .cometchat-fullscreen-viewer__nav-button:hover { + background: rgba(255, 255, 255, 0.3); + transform: scale(1.1); +} + +::ng-deep .cometchat-fullscreen-viewer__nav-button:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +/* Custom close button */ +::ng-deep .cometchat-fullscreen-viewer__close-button { + background: rgba(0, 0, 0, 0.5); + border-radius: 50%; + padding: 12px; +} + +::ng-deep .cometchat-fullscreen-viewer__close-button:hover { + background: rgba(0, 0, 0, 0.7); +} +``` + +### Custom Header Styling + +```css expandable +/* Custom header styling */ +::ng-deep .cometchat-fullscreen-viewer__header { + background: linear-gradient(180deg, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0) 100%); + padding: 20px; +} + +::ng-deep .cometchat-fullscreen-viewer__sender-name { + font-size: 16px; + font-weight: 600; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); +} + +::ng-deep .cometchat-fullscreen-viewer__index-display { + background: rgba(0, 0, 0, 0.6); + padding: 6px 12px; + border-radius: 16px; + backdrop-filter: blur(10px); +} +``` + +### Custom Backdrop Styling + +```css expandable +/* Custom backdrop */ +::ng-deep .cometchat-fullscreen-viewer__overlay { + background: rgba(0, 0, 0, 0.95); + backdrop-filter: blur(20px); +} + +/* Animated backdrop */ +::ng-deep .cometchat-fullscreen-viewer__overlay { + animation: fadeIn 0.3s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +``` + +## Accessibility + +The Fullscreen Viewer component is fully accessible and follows WCAG 2.1 Level AA guidelines. + +### WCAG 2.1 Compliance + +The component meets the following WCAG 2.1 Level AA success criteria: + +- ✅ **1.1.1 Non-text Content (Level A)**: All images and videos have descriptive ARIA labels +- ✅ **1.3.1 Info and Relationships (Level A)**: Proper semantic structure with dialog role +- ✅ **2.1.1 Keyboard (Level A)**: All functionality available via keyboard +- ✅ **2.1.2 No Keyboard Trap (Level A)**: Focus trap implemented correctly with escape mechanism +- ✅ **2.4.3 Focus Order (Level A)**: Logical focus order through interactive elements +- ✅ **2.4.7 Focus Visible (Level AA)**: Clear focus indicators on all interactive elements +- ✅ **4.1.2 Name, Role, Value (Level A)**: All elements have accessible names and roles + +## Picture-in-Picture (PIP) Mode + +The Fullscreen Viewer supports Picture-in-Picture (PIP) mode for videos, allowing users to minimize video playback into a floating window while browsing other content. This is an enterprise feature that enhances the video viewing experience. + +### Overview + +Picture-in-Picture mode enables: +- **Floating Video Window**: Minimize video to a small, always-on-top window +- **Multitasking**: Continue browsing while watching video +- **Browser Controls**: Use native browser PIP controls (play/pause, close) +- **Seamless Integration**: Works with both single video and gallery modes + +### Enabling PIP + +Enable PIP mode by setting the `enablePictureInPicture` input to `true`: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatFullScreenViewerComponent } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-pip-video', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` + + ` +}) +export class PipVideoComponent { + isViewerOpen = false; + videoUrl = 'https://example.com/video.mp4'; + + closeViewer(): void { + this.isViewerOpen = false; + } + + onPipStateChange(isPipMode: boolean): void { + console.log('PIP mode:', isPipMode ? 'active' : 'inactive'); + } +} +``` + +### PIP Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `enablePictureInPicture` | `boolean` | `true` | Enable/disable PIP functionality | +| `pipIconUrl` | `string` | `'assets/pip.svg'` | Custom PIP button icon URL | +| `pipExitIconUrl` | `string` | `'assets/pip_exit.svg'` | Custom PIP exit button icon URL | + +### PIP Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `pipStateChange` | `boolean` | Emitted when entering or exiting PIP mode. `true` when entering, `false` when exiting | + +### PIP with Gallery Mode + +PIP works seamlessly with gallery navigation: + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatFullScreenViewerComponent, MediaAttachment } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-pip-gallery', + standalone: true, + imports: [CometChatFullScreenViewerComponent], + template: ` + + ` +}) +export class PipGalleryComponent { + isViewerOpen = false; + isPipActive = false; + + videoAttachments: MediaAttachment[] = [ + { url: 'https://example.com/video1.mp4', type: 'video', name: 'Video 1' }, + { url: 'https://example.com/video2.mp4', type: 'video', name: 'Video 2' }, + { url: 'https://example.com/video3.mp4', type: 'video', name: 'Video 3' } + ]; + + closeViewer(): void { + this.isViewerOpen = false; + } + + onIndexChange(index: number): void { + // PIP automatically exits when navigating to another video + console.log('Navigated to video:', index + 1); + } + + onPipStateChange(isPipMode: boolean): void { + this.isPipActive = isPipMode; + console.log('PIP mode:', isPipMode ? 'active' : 'inactive'); + } +} +``` + +**Gallery Navigation Behavior:** +- When navigating to another video while in PIP mode, PIP automatically exits +- The new video loads in the fullscreen viewer +- User can re-enter PIP mode with the new video +- When navigating to an image, PIP exits (images don't support PIP) + +### Browser Support + +PIP requires browser support for the Picture-in-Picture API. The component automatically detects support and only shows the PIP button when available. + +**Supported Browsers:** +- ✅ Chrome 70+ +- ✅ Edge 79+ +- ✅ Safari 13.1+ +- ✅ Opera 57+ +- ❌ Firefox (limited support, requires user gesture) + +**Checking Support:** +```typescript +// The component checks this internally +const isPipSupported = document.pictureInPictureEnabled; +``` + +### PIP Button Visibility + +The PIP button is only visible when: +1. `enablePictureInPicture` is `true` +2. The current media is a video (not an image) +3. The browser supports the Picture-in-Picture API + +### PIP Behavior + +**Entering PIP:** +- Click the PIP button in the viewer header +- Video starts playing (if paused) and enters PIP mode +- Video continues playing in a floating window above all other content +- **Fullscreen viewer stays open** to keep the video element in the DOM +- The `pipStateChange` event is emitted with `true` + +**Exiting PIP:** +- Click the close button on the PIP window +- Use browser controls to close the PIP window +- The video will stop playing when PIP is closed +- The `pipStateChange` event is emitted with `false` + +**Automatic PIP Exit:** +- When navigating to another video in gallery mode +- When navigating to an image in gallery mode +- When closing the fullscreen viewer (via close button or Escape key) +- When the component is destroyed + + + The fullscreen viewer must stay open while PIP is active because the video element needs to remain in the DOM for PIP to work. If you close the viewer (via close button or Escape), the PIP window will also close. + + + + To use PIP effectively: click the PIP button, then minimize or switch away from the browser tab. The video will continue playing in the floating PIP window. + + +### Styling PIP Button + +Customize the PIP button appearance using CSS: + +```css expandable +/* PIP button styling */ +::ng-deep .cometchat-fullscreen-viewer__pip-button { + background: rgba(255, 255, 255, 0.2); + border-radius: 50%; + padding: 8px; + transition: all 0.2s ease; +} + +::ng-deep .cometchat-fullscreen-viewer__pip-button:hover { + background: rgba(255, 255, 255, 0.3); + transform: scale(1.1); +} + +/* Active PIP state */ +::ng-deep .cometchat-fullscreen-viewer__pip-button--active { + background: var(--cometchat-primary-color); +} +``` + +### PIP Limitations + + + PIP is only available for video content. Images do not support Picture-in-Picture. + + + + Only one video can be in PIP mode at a time (browser limitation). Entering PIP with a new video will exit PIP for any existing video. + + + + Some browsers require a user gesture (click) to enter PIP mode. The component handles this by using the PIP button click as the gesture. + + + + The video must be playing to enter PIP mode. The component automatically starts playback when you click the PIP button if the video is paused. + + + + Use the `pipStateChange` event to track PIP state and update your UI accordingly (e.g., show a "Video playing in PIP" indicator). + + +### Keyboard Support + +| Key | Action | Context | +|-----|--------|---------| +| `Tab` | Navigate forward through controls | Close → Download → Previous → Next | +| `Shift + Tab` | Navigate backward through controls | Reverse order | +| `Enter` | Activate focused button | Any button | +| `Space` | Activate focused button | Any button | +| `Right Arrow` | Navigate to next media | Gallery mode | +| `Left Arrow` | Navigate to previous media | Gallery mode | +| `Escape` | Close viewer | Anytime viewer is open | + +### ARIA Attributes + +The component automatically applies appropriate ARIA attributes: + +| Attribute | Element | Value | Purpose | +|-----------|---------|-------|---------| +| `role` | Viewer container | `"dialog"` | Identifies as modal dialog | +| `aria-modal` | Viewer container | `"true"` | Indicates modal behavior | +| `aria-label` | Viewer container | `"Fullscreen media viewer"` | Provides accessible name | +| `aria-label` | Close button | Localized "Close" | Describes close action | +| `aria-label` | Download button | Localized "Download" | Describes download action | +| `aria-label` | Previous button | Localized "Previous" | Describes previous action | +| `aria-label` | Next button | Localized "Next" | Describes next action | +| `aria-disabled` | Navigation buttons | `"true"` when disabled | Indicates disabled state | +| `aria-live` | Index display | `"polite"` | Announces position changes | +| `alt` | Image elements | Descriptive text | Provides image description | + +### Screen Reader Behavior + +Screen readers announce the fullscreen viewer with: +1. **Opening**: "Dialog, Fullscreen media viewer" +2. **Image display**: "Image from [sender name]" +3. **Video display**: "Video from [sender name]" +4. **Navigation**: "Button, Next" / "Button, Previous" +5. **Position changes**: "3 of 10" (via aria-live region) +6. **Disabled buttons**: "Button, Next, disabled" +7. **Closing**: Focus returns to triggering element + +### Focus Management + +The component implements comprehensive focus management: + +**On Open:** +- Focus moves to close button +- Focus is trapped within the viewer +- Tab cycles through: Close → Download → Previous → Next → Close + +**On Navigate:** +- Focus remains on the control that triggered navigation +- Position change announced via aria-live region + +**On Close:** +- Focus returns to the element that opened the viewer +- Body scroll is restored +- Focus trap is removed + +### Accessibility Best Practices + + + The component automatically handles all accessibility requirements. No additional ARIA attributes or keyboard handling is needed. + + + + When opening the viewer programmatically, consider passing the triggering element reference to ensure proper focus restoration. + + + + All interactive elements have visible focus indicators that meet WCAG 2.1 Level AA contrast requirements. + + + + The focus trap ensures keyboard users can't accidentally tab out of the viewer, improving usability and accessibility. + + +## Best Practices + + + Use gallery mode (`attachments` array) for multiple media items instead of opening the viewer multiple times. + + + + The viewer prevents body scroll while open. Avoid programmatically scrolling the page while the viewer is active. + + + + Videos automatically stop when navigating away or closing the viewer. No manual cleanup is required. + + + + Handle the `indexChange` event to track user navigation through the gallery for analytics or state management. + + + + The `isOpen` property controls visibility. Always set it to `false` in your `closeClick` handler to properly close the viewer. + + + + The viewer works in both single mode (backward compatible with `url` prop) and gallery mode (`attachments` array). + + + + Provide sender information (`senderName` and `senderAvatarUrl`) for better context, especially in chat applications. + + + + Ensure media URLs are accessible and not blocked by CORS policies. The viewer cannot display media from restricted sources. + + +## Examples + +### Complete Gallery Component + +```typescript expandable +import { Component, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatFullScreenViewerComponent, + MediaAttachment +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-media-gallery', + standalone: true, + imports: [CommonModule, CometChatFullScreenViewerComponent], + template: ` + + + + `, + styles: [` + .gallery-container { + padding: 20px; + } + + .thumbnail-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 16px; + margin-top: 20px; + } + + .thumbnail-item { + position: relative; + cursor: pointer; + border-radius: 8px; + overflow: hidden; + aspect-ratio: 1; + transition: transform 0.2s; + } + + .thumbnail-item:hover { + transform: scale(1.05); + } + + .thumbnail-item img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .video-thumbnail { + position: relative; + width: 100%; + height: 100%; + } + + .play-overlay { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 60px; + height: 60px; + background: rgba(0, 0, 0, 0.7); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 24px; + } + `] +}) +export class MediaGalleryComponent implements OnInit { + isViewerOpen = false; + selectedIndex = 0; + senderName = ''; + senderAvatar = ''; + + mediaItems: MediaAttachment[] = []; + + async ngOnInit(): Promise { + await this.loadMediaFromMessages(); + } + + async loadMediaFromMessages(): Promise { + try { + const messagesRequest = new CometChat.MessagesRequestBuilder() + .setLimit(50) + .setTypes([CometChat.MESSAGE_TYPE.IMAGE, CometChat.MESSAGE_TYPE.VIDEO]) + .build(); + + const messages = await messagesRequest.fetchPrevious(); + + // Extract media attachments from messages + this.mediaItems = messages.flatMap((msg: any) => { + const attachments = msg.getAttachments() || []; + return attachments.map((att: any) => ({ + url: att.url, + type: msg.getType() === 'image' ? 'image' as const : 'video' as const, + name: att.name, + thumbnail: att.thumbnail, + mimeType: att.mimeType, + size: att.size + })); + }); + + // Get sender info from first message + if (messages.length > 0) { + const sender = messages[0].getSender(); + this.senderName = sender.getName(); + this.senderAvatar = sender.getAvatar(); + } + } catch (error) { + console.error('Error loading media:', error); + } + } + + openGallery(index: number): void { + this.selectedIndex = index; + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } + + onIndexChange(index: number): void { + this.selectedIndex = index; + console.log('Viewing media:', this.mediaItems[index].name); + } + + onDownload(media: MediaAttachment | string): void { + console.log('Downloading:', media); + // Implement download logic + } +} +``` + +### Integration with Image Bubble + +```typescript expandable +import { Component, Input } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatImageBubbleComponent, + CometChatFullScreenViewerComponent, + MediaAttachment +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-image-message', + standalone: true, + imports: [CometChatImageBubbleComponent, CometChatFullScreenViewerComponent], + template: ` + + + + ` +}) +export class ImageMessageComponent { + @Input() message!: CometChat.MediaMessage; + + isViewerOpen = false; + selectedIndex = 0; + attachments: MediaAttachment[] = []; + + openViewer(event: { attachment: MediaAttachment; index: number }): void { + // Extract all attachments from message + const messageAttachments = this.message.getAttachments(); + this.attachments = messageAttachments.map(att => ({ + url: att.url, + type: 'image' as const, + name: att.name, + mimeType: att.mimeType, + size: att.size + })); + + this.selectedIndex = event.index; + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } +} +``` + +### Integration with Video Bubble + +```typescript expandable +import { Component, Input } from '@angular/core'; +import { CometChat } from '@cometchat/chat-sdk-javascript'; +import { + CometChatVideoBubbleComponent, + CometChatFullScreenViewerComponent, + MediaAttachment +} from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-video-message', + standalone: true, + imports: [CometChatVideoBubbleComponent, CometChatFullScreenViewerComponent], + template: ` + + + + ` +}) +export class VideoMessageComponent { + @Input() message!: CometChat.MediaMessage; + + isViewerOpen = false; + selectedIndex = 0; + attachments: MediaAttachment[] = []; + + openViewer(event: { attachment: MediaAttachment; index: number }): void { + // Extract all video attachments from message + const messageAttachments = this.message.getAttachments(); + this.attachments = messageAttachments.map(att => ({ + url: att.url, + type: 'video' as const, + name: att.name, + mimeType: att.mimeType, + size: att.size + })); + + this.selectedIndex = event.index; + this.isViewerOpen = true; + } + + closeViewer(): void { + this.isViewerOpen = false; + } +} +``` + +## Related Components + +- **CometChatImageBubble**: Displays image messages with grid layouts and uses fullscreen viewer for gallery +- **CometChatVideoBubble**: Displays video messages with thumbnails and uses fullscreen viewer for playback +- **CometChatFileBubble**: Displays file attachments with download functionality +- **CometChatMessageBubble**: Container component that uses media bubbles for rendering +- **CometChatMessageList**: Displays lists of messages including media messages + +## Technical Details + +- **Standalone Component**: Can be imported and used independently +- **Change Detection**: Uses OnPush change detection strategy for optimal performance +- **Dependencies**: + - Angular CommonModule + - CometChat SDK for message types (optional) + - CometChatAvatar for sender display + - TranslatePipe for localization +- **Bundle Size**: Minimal footprint (~15KB) +- **BEM CSS**: Follows Block Element Modifier naming convention +- **Accessibility**: WCAG 2.1 Level AA compliant +- **Browser Support**: Modern browsers with ES6+ support + +## Performance Considerations + +### Optimization Strategies + +**Change Detection:** +- Uses OnPush strategy to minimize unnecessary re-renders +- Only updates when inputs change or events are emitted + +**Video Management:** +- Videos only load when displayed (not preloaded) +- Automatic video stop prevents memory leaks +- Video elements cleaned up on navigation and close + +**Body Scroll Management:** +- Efficient CSS-based scroll prevention +- Original scroll position preserved and restored +- No layout thrashing or reflows + +**Focus Management:** +- Minimal DOM queries for focus trap +- Focus restoration uses stored reference +- No unnecessary focus changes during navigation + +**Keyboard Handling:** +- Single global keyboard listener +- Event delegation for efficiency +- Listener removed on component destruction + +### Best Practices for Performance + + + The component uses OnPush change detection. Ensure attachment arrays are immutable for optimal performance. + + + + Videos are not preloaded. They only start loading when displayed in the viewer, saving bandwidth. + + + + Avoid frequently toggling `isOpen`. Each open/close cycle manages body scroll and focus, which has a small performance cost. + + + + The component automatically cleans up all resources (video elements, event listeners, body scroll) on destruction. + + + +For troubleshooting tips, see the [Troubleshooting Guide](/troubleshooting#cometchatfullscreenviewer). + + +## FAQ + +### Q: Can I use this component outside of CometChat message contexts? + +**A:** Yes! The component is standalone and can be used anywhere you need fullscreen media viewing. Just provide the `attachments` array or `url` property with your media. + +### Q: How do I customize the viewer's appearance? + +**A:** Use CSS variables for theming or `::ng-deep` for deep styling: + +```css +cometchat-fullscreen-viewer { + --cometchat-background-color-01: rgba(0, 0, 0, 0.95); +} + +::ng-deep .cometchat-fullscreen-viewer__nav-button { + background: rgba(255, 255, 255, 0.3); +} +``` + +### Q: Can I disable specific features like download or navigation? + +**A:** The component doesn't have props to disable features, but you can hide them with CSS: + +```css +/* Hide download button */ +::ng-deep .cometchat-fullscreen-viewer__download-button { + display: none; +} + +/* Hide navigation buttons */ +::ng-deep .cometchat-fullscreen-viewer__nav-button { + display: none; +} +``` + +### Q: Does the viewer support image zoom or pinch-to-zoom? + +**A:** The current version displays images at their full size. Advanced zoom features like pinch-to-zoom are not currently supported but may be added in future versions. + +### Q: How do I handle download events? + +**A:** Listen to the `downloadClick` event: + +```typescript expandable +onDownload(media: MediaAttachment | string): void { + if (typeof media === 'string') { + // Single mode - media is URL string + window.open(media, '_blank'); + } else { + // Gallery mode - media is MediaAttachment object + const link = document.createElement('a'); + link.href = media.url; + link.download = media.name || 'download'; + link.click(); + } +} +``` + +### Q: Can I programmatically navigate to a specific index? + +**A:** Yes, update the `startIndex` property: + +```typescript +// Navigate to third item +this.startIndex = 2; +``` + +Note: This only works when the viewer is opening. To navigate while open, you'll need to close and reopen with a new `startIndex`. + +### Q: Does the viewer work on mobile devices? + +**A:** Yes! The viewer is fully responsive and works on mobile devices. Touch gestures for navigation are not currently supported, but the navigation buttons work on touch screens. + +### Q: How do I track which media item the user is viewing? + +**A:** Listen to the `indexChange` event: + +```typescript +onIndexChange(index: number): void { + console.log('User is viewing item:', index + 1); + // Track analytics, update state, etc. +} +``` + +### Q: Can I use this with non-CometChat media sources? + +**A:** Absolutely! The component works with any valid image or video URLs: + +```typescript +const myMedia: MediaAttachment[] = [ + { url: 'https://my-cdn.com/photo1.jpg', type: 'image', name: 'Photo 1' }, + { url: 'https://my-cdn.com/video1.mp4', type: 'video', name: 'Video 1' } +]; +``` + +### Q: What video formats are supported? + +**A:** The viewer uses HTML5 video element, which supports: +- **MP4** (H.264 codec) - Best compatibility +- **WebM** (VP8/VP9 codec) +- **Ogg** (Theora codec) + +Support varies by browser. MP4 with H.264 has the widest support. + +### Q: How do I use Picture-in-Picture mode? + +**A:** Enable PIP by setting `enablePictureInPicture` to `true`: + +```typescript + +``` + +The PIP button appears in the header for videos when the browser supports PIP. Click it to minimize the video to a floating window. + +### Q: Why isn't the PIP button showing? + +**A:** The PIP button only appears when: +1. `enablePictureInPicture` is `true` (default) +2. The current media is a video (not an image) +3. The browser supports the Picture-in-Picture API + +Check browser support: Chrome 70+, Edge 79+, Safari 13.1+, Opera 57+. Firefox has limited support. + +### Q: How do I prevent the viewer from closing when clicking the backdrop? + +**A:** The viewer currently closes when clicking the backdrop (this is standard modal behavior). To prevent this, you would need to modify the component or handle the `closeClick` event conditionally. + +### Q: Can I add custom buttons to the viewer header? + +**A:** The component doesn't support custom buttons via props. You would need to use CSS to position custom elements or fork the component for extensive customization. + +## Changelog + +### v5.0.0 (Current) +- ✨ **New**: Unified fullscreen viewer for images and videos +- ✨ **New**: Gallery mode with navigation controls +- ✨ **New**: Keyboard navigation (Arrow keys, Escape) +- ✨ **New**: Sender information display in header +- ✨ **New**: Position indicator (X of Y) +- ✨ **New**: Automatic video playback management +- ✨ **New**: Focus trap and focus restoration +- ✨ **New**: Body scroll prevention +- ✨ **New**: Full WCAG 2.1 Level AA accessibility +- ✨ **New**: Picture-in-Picture (PIP) mode for videos (Enterprise feature) +- ✨ **New**: PIP state change events for tracking PIP mode +- ✨ **New**: Browser support detection for PIP +- 🔄 **Changed**: Replaces `image-gallery-viewer` and `video-player-viewer` +- 🔄 **Changed**: New API with `attachments` array for gallery mode +- 🔄 **Changed**: Backward compatible with single mode (`url` prop) + +### Deprecated Components +- ⚠️ **Deprecated**: `image-gallery-viewer` - Use `CometChatFullscreenViewer` instead +- ⚠️ **Deprecated**: `video-player-viewer` - Use `CometChatFullscreenViewer` instead + +These deprecated components will be removed in v6.0.0. Please migrate to `CometChatFullscreenViewer`. + +## Support + +For issues, questions, or feature requests: +- 📖 [Documentation](https://www.cometchat.com/docs/angular-uikit) +- 💬 [Community Forum](https://forum.cometchat.com) +- 🐛 [GitHub Issues](https://github.com/cometchat/cometchat-chat-uikit-angular) +- 📧 [Support Email](mailto:support@cometchat.com) diff --git a/ui-kit/angular/v5/components/cometchat-group-item.mdx b/ui-kit/angular/components/cometchat-group-item.mdx similarity index 96% rename from ui-kit/angular/v5/components/cometchat-group-item.mdx rename to ui-kit/angular/components/cometchat-group-item.mdx index 59ce61f32..567bc8c1d 100644 --- a/ui-kit/angular/v5/components/cometchat-group-item.mdx +++ b/ui-kit/angular/components/cometchat-group-item.mdx @@ -156,5 +156,5 @@ Provide custom templates via inputs to override any visual section: ## Related Components - [CometChatGroups](./cometchat-groups.mdx) — Parent list component that renders multiple group items -- [CometChatAvatar](/ui-kit/angular/v5/components/cometchat-users) — Used internally for the group avatar -- [CometChatContextMenu](/ui-kit/angular/v5/components/cometchat-message-list) — Used internally for the context menu +- [CometChatAvatar](/ui-kit/angular/components/cometchat-users) — Used internally for the group avatar +- [CometChatContextMenu](/ui-kit/angular/components/cometchat-message-list) — Used internally for the context menu diff --git a/ui-kit/angular/v5/components/cometchat-group-member-item.mdx b/ui-kit/angular/components/cometchat-group-member-item.mdx similarity index 97% rename from ui-kit/angular/v5/components/cometchat-group-member-item.mdx rename to ui-kit/angular/components/cometchat-group-member-item.mdx index 8990c7dac..31494049c 100644 --- a/ui-kit/angular/v5/components/cometchat-group-member-item.mdx +++ b/ui-kit/angular/components/cometchat-group-member-item.mdx @@ -186,7 +186,7 @@ getContextMenuOptions(member: CometChat.GroupMember): CometChatOption[] { ## Related Components - [CometChatGroupMembers](./cometchat-group-members.mdx) — Parent list component that renders multiple group member items -- [CometChatAvatar](/ui-kit/angular/v5/components/cometchat-users) — Used internally for the member avatar -- [CometChatContextMenu](/ui-kit/angular/v5/components/cometchat-message-list) — Used internally for the context menu +- [CometChatAvatar](/ui-kit/angular/components/cometchat-users) — Used internally for the member avatar +- [CometChatContextMenu](/ui-kit/angular/components/cometchat-message-list) — Used internally for the context menu - [CometChatUserItem](./cometchat-user-item.mdx) — Similar component for rendering user items - [CometChatGroupItem](./cometchat-group-item.mdx) — Similar component for rendering group items diff --git a/ui-kit/angular/v5/components/cometchat-group-members.mdx b/ui-kit/angular/components/cometchat-group-members.mdx similarity index 99% rename from ui-kit/angular/v5/components/cometchat-group-members.mdx rename to ui-kit/angular/components/cometchat-group-members.mdx index 10af28ae3..cecaf4b80 100644 --- a/ui-kit/angular/v5/components/cometchat-group-members.mdx +++ b/ui-kit/angular/components/cometchat-group-members.mdx @@ -175,7 +175,7 @@ export class GroupMembersServiceComponent { } ``` -See the [ChatStateService API reference](/ui-kit/angular/v5/api-reference/chat-state-service) for the full list of signals, observables, and setter methods. +See the [ChatStateService API reference](/ui-kit/angular/api-reference/chat-state-service) for the full list of signals, observables, and setter methods. This is the recommended approach for most applications. It reduces boilerplate @@ -772,7 +772,7 @@ handleError(error: CometChat.CometChatException): void { ``` -For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/v5/troubleshooting#cometchatgroupmembers). +For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchatgroupmembers). ## Best Practices diff --git a/ui-kit/angular/v5/components/cometchat-groups.mdx b/ui-kit/angular/components/cometchat-groups.mdx similarity index 99% rename from ui-kit/angular/v5/components/cometchat-groups.mdx rename to ui-kit/angular/components/cometchat-groups.mdx index 80ae60f48..f4877830d 100644 --- a/ui-kit/angular/v5/components/cometchat-groups.mdx +++ b/ui-kit/angular/components/cometchat-groups.mdx @@ -692,7 +692,7 @@ handleError(error: CometChat.CometChatException): void { ``` -For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/v5/troubleshooting#cometchatgroups). +For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchatgroups). ## Best Practices diff --git a/ui-kit/angular/v5/components/cometchat-image-bubble.mdx b/ui-kit/angular/components/cometchat-image-bubble.mdx similarity index 99% rename from ui-kit/angular/v5/components/cometchat-image-bubble.mdx rename to ui-kit/angular/components/cometchat-image-bubble.mdx index 36faf6f42..cfdef4cb4 100644 --- a/ui-kit/angular/v5/components/cometchat-image-bubble.mdx +++ b/ui-kit/angular/components/cometchat-image-bubble.mdx @@ -539,6 +539,6 @@ Screen readers announce the image bubble with: -For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/v5/troubleshooting#cometchatimagebubble). +For troubleshooting tips, see the [Troubleshooting Guide](/ui-kit/angular/troubleshooting#cometchatimagebubble). diff --git a/ui-kit/angular/v5/components/cometchat-incoming-call.mdx b/ui-kit/angular/components/cometchat-incoming-call.mdx similarity index 96% rename from ui-kit/angular/v5/components/cometchat-incoming-call.mdx rename to ui-kit/angular/components/cometchat-incoming-call.mdx index b86941740..15dbc27fd 100644 --- a/ui-kit/angular/v5/components/cometchat-incoming-call.mdx +++ b/ui-kit/angular/components/cometchat-incoming-call.mdx @@ -150,6 +150,6 @@ cometchat-incoming-call { ## Related Components -- [CometChatOngoingCall](/ui-kit/angular/v5/components/cometchat-outgoing-call) — Rendered automatically after accepting a call +- [CometChatOngoingCall](/ui-kit/angular/components/cometchat-outgoing-call) — Rendered automatically after accepting a call - [CometChatCallButtons](./cometchat-call-buttons.mdx) — Initiates outgoing calls -- [CometChatListItem](/ui-kit/angular/v5/components/cometchat-user-item) — Used internally to render the caller information +- [CometChatListItem](/ui-kit/angular/components/cometchat-user-item) — Used internally to render the caller information diff --git a/ui-kit/angular/components/cometchat-link-dialog.mdx b/ui-kit/angular/components/cometchat-link-dialog.mdx new file mode 100644 index 000000000..ed0bc3042 --- /dev/null +++ b/ui-kit/angular/components/cometchat-link-dialog.mdx @@ -0,0 +1,174 @@ +--- +title: "CometChatLinkDialog" +description: "A dialog component for adding and editing hyperlinks in the rich text message composer" +--- +The `CometChatLinkDialog` component provides a modal dialog for adding or editing hyperlinks within the rich text editor. It includes input fields for link text and URL, with validation, and supports both "add" and "edit" modes. + +## Overview + +The Link Dialog component provides: + +- **Add Mode**: Create a new hyperlink with text and URL fields +- **Edit Mode**: Modify an existing link's text and URL, with an additional "Remove" action +- **URL Validation**: Validates URL format before saving and auto-prepends `https://` if no protocol is specified +- **Pre-filled Values**: Supports initial text from editor selection or existing link data +- **Focus Trap**: Keeps keyboard focus within the dialog while open +- **Focus Restoration**: Returns focus to the previously focused element on close + + + + **Live Preview** — default link dialog preview. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-link-dialog--default) + + + + +## Basic Usage + +### Add Link Mode + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatLinkDialogComponent, LinkData } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-link-dialog-demo', + standalone: true, + imports: [CometChatLinkDialogComponent], + template: ` + + + ` +}) +export class LinkDialogAddDemoComponent { + onSave(linkData: LinkData): void { + console.log('Link saved:', linkData.text, linkData.url); + } + + onCancel(): void { + console.log('Link dialog cancelled'); + } +} +``` + +### Edit Link Mode + +```typescript expandable +@Component({ + selector: 'app-link-dialog-edit-demo', + standalone: true, + imports: [CometChatLinkDialogComponent], + template: ` + + + ` +}) +export class LinkDialogEditDemoComponent { + onSave(linkData: LinkData): void { + console.log('Link updated:', linkData); + } + + onRemove(): void { + console.log('Link removed'); + } + + onCancel(): void { + console.log('Edit cancelled'); + } +} +``` + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `mode` | `'add' \| 'edit'` | `'add'` | Dialog mode — `'add'` for creating new links, `'edit'` for modifying existing links | +| `initialText` | `string` | `''` | Initial text value for the link text field (used in edit mode or pre-filled add mode) | +| `initialUrl` | `string` | `''` | Initial URL value for the URL field (used in edit mode) | +| `selectedText` | `string` | `''` | Selected text from the editor, used as the default link text in add mode | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `save` | `LinkData` (`{ text: string; url: string }`) | Emitted when the save button is clicked with valid link data | +| `cancel` | `void` | Emitted when the cancel button is clicked or Escape is pressed | +| `remove` | `void` | Emitted when the remove button is clicked (edit mode only) | + +## Customization + +### CSS Variables + +The Link Dialog uses BEM-style CSS classes with CSS variable overrides: + +```css expandable +/* Dialog container */ +.cometchat-link-dialog { + background: var(--cometchat-background-color-01); + border-radius: var(--cometchat-radius-3); +} + +/* Title */ +.cometchat-link-dialog__title { + font: var(--cometchat-font-heading4-bold); + color: var(--cometchat-text-color-primary); +} + +/* Input fields */ +.cometchat-link-dialog__input { + font: var(--cometchat-font-body-regular); + color: var(--cometchat-text-color-primary); + border: 1px solid var(--cometchat-border-color-light); + border-radius: var(--cometchat-radius-2); +} + +/* Error message */ +.cometchat-link-dialog__error { + font: var(--cometchat-font-caption1-regular); + color: var(--cometchat-error-color); +} +``` + +## Accessibility + +### Keyboard Navigation + +- **Tab / Shift+Tab**: Cycles focus through all interactive elements within the dialog (focus trap) +- **Escape**: Closes the dialog and restores focus to the previously focused element +- **Enter**: Saves the link when pressed inside an input field (if valid) + +### Focus Management + +- Focus is trapped within the dialog while it is open +- In add mode with no text, initial focus is set to the text input +- In edit mode (or add mode with pre-filled text), initial focus is set to the URL input with text selected +- On close, focus is restored to the element that was focused before the dialog opened + +### Screen Reader Support + +- The dialog uses `role="dialog"` with `aria-modal="true"` +- `aria-labelledby` references the dialog title +- Validation error messages are linked via `aria-describedby` +- Input fields include associated labels + +## Related Components + +- [CometChatMessageComposer](/ui-kit/angular/components/cometchat-message-composer) - Parent component that opens this dialog for link insertion +- [CometChatLinkPopover](/ui-kit/angular/components/cometchat-link-popover) - Inline popover for quick link edit/remove actions diff --git a/ui-kit/angular/components/cometchat-link-popover.mdx b/ui-kit/angular/components/cometchat-link-popover.mdx new file mode 100644 index 000000000..375d86f7a --- /dev/null +++ b/ui-kit/angular/components/cometchat-link-popover.mdx @@ -0,0 +1,140 @@ +--- +title: "CometChatLinkPopover" +description: "A small popover component that appears on links in the editor, providing quick Edit and Remove actions" +--- +The `CometChatLinkPopover` component displays a small floating popover when a user clicks on a link in the rich text editor. It provides "Edit" and "Remove" buttons for quick link management, and automatically positions itself relative to the click coordinates while staying within the viewport. + +## Overview + +The Link Popover component provides: + +- **Edit Action**: Opens the link dialog to modify the link text and URL +- **Remove Action**: Removes the hyperlink from the selected text +- **Auto-Positioning**: Calculates position based on click coordinates and adjusts to stay within the viewport +- **Backdrop Dismiss**: Clicking outside the popover closes it +- **Keyboard Navigation**: Arrow keys navigate between Edit and Remove buttons; Escape and Tab close the popover +- **Focus Management**: Auto-focuses the Edit button on open and restores focus on close + + + + **Live Preview** — default link popover preview. + [Open in Storybook ↗](https://storybook.cometchat.io/angular/?path=/story/components-misc-link-popover--default) + + + + +## Basic Usage + +```typescript expandable +import { Component } from '@angular/core'; +import { CometChatLinkPopoverComponent, LinkPopoverData } from '@cometchat/chat-uikit-angular'; + +@Component({ + selector: 'app-link-popover-demo', + standalone: true, + imports: [CometChatLinkPopoverComponent], + template: ` + + + ` +}) +export class LinkPopoverDemoComponent { + clickX = 200; + clickY = 300; + + onEdit(data: LinkPopoverData): void { + console.log('Edit link:', data.url, data.text); + } + + onRemove(): void { + console.log('Link removed'); + } + + onClose(): void { + console.log('Popover closed'); + } +} +``` + +## Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `url` | `string` | `''` | The URL of the link to display and pass to the edit action | +| `text` | `string` | `''` | The display text of the link | +| `x` | `number` | `0` | X coordinate (in pixels) for positioning the popover | +| `y` | `number` | `0` | Y coordinate (in pixels) for positioning the popover | +| `position` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | Preferred position of the popover relative to the anchor point | + +## Events + +| Event | Payload Type | Description | +|-------|-------------|-------------| +| `editClick` | `LinkPopoverData` (`{ url: string; text: string }`) | Emitted when the Edit button is clicked, carrying the current link URL and text | +| `removeClick` | `void` | Emitted when the Remove button is clicked | +| `closeClick` | `void` | Emitted when the popover should close (backdrop click, Escape, or Tab) | + +## Customization + +### CSS Variables + +The Link Popover uses BEM-style CSS classes with CSS variable overrides: + +```css expandable +/* Popover container */ +.cometchat-link-popover { + background: var(--cometchat-background-color-01); + border-radius: var(--cometchat-radius-2); + padding: var(--cometchat-spacing-1) var(--cometchat-spacing-2); +} + +/* Action buttons */ +.cometchat-link-popover__button { + font: var(--cometchat-font-caption1-medium); + color: var(--cometchat-text-color-primary); +} + +/* Backdrop overlay */ +.cometchat-link-popover__backdrop { + background: transparent; +} +``` + +## Accessibility + +### Keyboard Navigation + +- **ArrowDown / ArrowRight**: Moves focus to the next button (wraps around) +- **ArrowUp / ArrowLeft**: Moves focus to the previous button (wraps around) +- **Escape**: Closes the popover and restores focus +- **Tab**: Closes the popover and restores focus (no focus trap — simple menu pattern) +- **Enter / Space**: Activates the focused button + +### Focus Management + +- The Edit button receives focus automatically when the popover opens +- On close (destroy), focus is restored to the element that was focused before the popover appeared + +### Screen Reader Support + +- Buttons include descriptive labels for Edit and Remove actions +- The popover uses `role="menu"` semantics for the button group + +## Related Components + +- [CometChatLinkDialog](/ui-kit/angular/components/cometchat-link-dialog) - Full dialog for adding or editing links (opened from the Edit action) +- [CometChatMessageComposer](/ui-kit/angular/components/cometchat-message-composer) - Parent component that manages link popover visibility diff --git a/ui-kit/angular/components/cometchat-markdown-renderer.mdx b/ui-kit/angular/components/cometchat-markdown-renderer.mdx new file mode 100644 index 000000000..0d202021f --- /dev/null +++ b/ui-kit/angular/components/cometchat-markdown-renderer.mdx @@ -0,0 +1,250 @@ +--- +title: "CometChatMarkdownRenderer" +description: "Zero-dependency Angular component that parses and renders markdown to HTML with streaming support, XSS sanitization, and a copy button per code block." +--- + +## Overview + +`CometChatMarkdownRenderer` converts a markdown string to sanitized HTML using a custom TypeScript parser — no third-party markdown library is required. It is used internally by `CometChatAIAssistantMessageBubble` (completed messages) and `CometChatStreamMessageBubble` (live streaming), but can be used standalone anywhere you need markdown rendering. + +Key capabilities: + +- **Zero dependencies** — custom `CometChatMarkdownParser` class; no `marked`, `remark`, or `highlight.js` +- **Streaming-safe** — when `streaming` is `true`, incomplete syntax at the end of the text is rendered as plain text rather than dropped or throwing an error +- **XSS sanitization** — all raw HTML in the input is escaped before markdown rules are applied; only the supported markdown constructs produce HTML elements +- **Code block copy** — each fenced code block gets a copy button; state is tracked per-block as an independent `WritableSignal` +- **Image click** — clicking a rendered image emits `imageClick` with the image URL +- **Extensible parser** — `CometChatMarkdownParser` is exported from the public API so you can extend it with custom node types + +## Supported Markdown Constructs + +| Syntax | Output | +|--------|--------| +| `# Heading` through `###### Heading` | `

` – `

` | +| `**bold**` | `` | +| `_italic_` | `` | +| `~~strikethrough~~` | `` | +| `` `inline code` `` | `` | +| ` ```lang\ncode\n``` ` | `
` |
+| `> blockquote` | `
` | +| `1. item` | `
  1. ` | +| `- item` or `* item` | `

E%8LW3lkzWdsWAi9C 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�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