Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -4589,7 +4589,10 @@
"sdk/flutter/edit-message",
"sdk/flutter/flag-message",
"sdk/flutter/delete-message",
"sdk/flutter/pin-messages",
"sdk/flutter/save-messages",
"sdk/flutter/delete-conversation",
"sdk/flutter/pin-conversations",
"sdk/flutter/typing-indicators",
"sdk/flutter/transient-messages",
"sdk/flutter/delivery-read-receipts",
Expand Down
106 changes: 106 additions & 0 deletions sdk/flutter/pin-conversations.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
title: "Pin Conversations"
description: "Pin CometChat conversations to the top of the list in Flutter apps and keep every device in sync through real-time pin events."
---



Pinning a conversation surfaces it at the top of the logged-in user's conversation list. Conversation pins are **per-user** — pinning a conversation does not affect how the other participants see their lists. A pinned conversation carries a `pinnedAt` timestamp and the `pinnedBy` uid.

A conversation can also be pinned for the user by an admin surface, in which case `pinnedBy` carries the `app_system` sentinel. System pins rank above the user's own pins and cannot be removed from the client.

## Pin a Conversation

In order to pin a conversation, you can use the `pinConversation()` method. This method takes the uid/guid of the conversation counterpart and the conversation type (`user`/`group`). On success it returns the full updated `Conversation` with `pinnedAt` and `pinnedBy` stamped.

<Tabs>
<Tab title="Dart">
```dart
String conversationWith = "cometchat-uid-1";
String conversationType = CometChatConversationType.user;

CometChat.pinConversation(conversationWith, conversationType,
onSuccess: (Conversation conversation) {
debugPrint("Conversation pinned at: ${conversation.pinnedAt}");
}, onError: (CometChatException e) {
debugPrint("Conversation pinning failed with exception: ${e.message}");
});
```

</Tab>
</Tabs>

The call is idempotent — pinning an already-pinned conversation succeeds and returns the current state.

## Unpin a Conversation

In order to unpin a conversation, you can use the `unpinConversation()` method. Only a pin placed by the logged-in user can be removed — an `app_system` pin is rejected server-side. The returned `Conversation` carries the pin fields cleared to `null`.

<Tabs>
<Tab title="Dart">
```dart
String conversationWith = "cometchat-uid-1";
String conversationType = CometChatConversationType.user;

CometChat.unpinConversation(conversationWith, conversationType,
onSuccess: (Conversation conversation) {
debugPrint("Conversation unpinned");
}, onError: (CometChatException e) {
debugPrint("Conversation unpinning failed with exception: ${e.message}");
});
```

</Tab>
</Tabs>

## Real-Time Pin Events

Pin and unpin events are delivered to the logged-in user's devices through the `ConversationListener` class — the acting device receives the callback on success, and the user's other devices receive it over the socket, so lists stay in sync everywhere. Admin (`app_system`) pins applied server-side arrive through the same callbacks.

To receive them, register a listener using the `addConversationListener()` method and override the `onConversationPinned()` and `onConversationUnpinned()` callbacks. Remove the listener with `removeConversationListener()` when it is no longer needed.

<Tabs>
<Tab title="Dart">
```dart
class Class_Name with ConversationListener {

//CometChat.addConversationListener("listenerId", this);

@override
void onConversationPinned(Conversation conversation) {
debugPrint("Conversation pinned: ${conversation.conversationId}");
}

@override
void onConversationUnpinned(Conversation conversation) {
debugPrint("Conversation unpinned: ${conversation.conversationId}");
}
}
```

</Tab>
</Tabs>

When applying these events to a conversation list, keep the ordering contract: system pins (`pinnedBy == "app_system"`) stay above user pins, and user pins stay above the activity-ordered rest of the list.

## Fetching and Ordering

Pinned conversations are returned by the regular `ConversationsRequest` described in [Retrieve Conversations](/sdk/flutter/retrieve-conversations), ordered pinned-first — system pins, then the user's pins, then the remaining conversations by latest activity. Inspect `conversation.pinnedAt` / `conversation.pinnedBy` on the fetched objects to render the pinned state.

## Feature Availability and Limits

Whether the Pin Conversation feature is enabled for the logged-in user is served on the user's login payload. You can check it at any time using the synchronous `isPinConversationEnabled()` method — it never throws, and returns `true` when the backend did not serve the flag.

The maximum number of conversations a user can pin is available through `getPinnedConversationsLimit()`, which returns `null` when the backend did not serve a limit. When a pin call exceeds the cap, it fails with a limit-exceeded error whose `errorParams` map carries the authoritative limit as `{"limit": n}`.

<Tabs>
<Tab title="Dart">
```dart
if (CometChat.isPinConversationEnabled()) {
int? limit = CometChat.getPinnedConversationsLimit();
debugPrint("Conversation pinning enabled, limit: ${limit ?? "server default"}");
}
```

</Tab>
</Tabs>
146 changes: 146 additions & 0 deletions sdk/flutter/pin-messages.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
title: "Pin Messages"
description: "Pin and unpin CometChat messages in Flutter apps, listen to pin events in real time, and fetch the pinned messages of a conversation."
---



Pinning highlights an important message for **everyone in the conversation**. A pinned message carries a `pinnedAt` timestamp and the `pinnedBy` uid of the member who pinned it, and every participant can fetch the conversation's pinned list.

Pinning is permissioned in groups — only participants with the admin, moderator or owner scope can pin or unpin. In one-to-one conversations both participants can.

## Pin a Message

*In other words, as a member of a conversation, how do I pin a message for everyone?*

In order to pin a message, you can use the `pinMessage()` method. This method takes the id of the message to be pinned. On success it returns the **full updated message** with `pinnedAt` and `pinnedBy` stamped.

<Tabs>
<Tab title="Dart">
```dart
int messageId = 103;

CometChat.pinMessage(messageId, onSuccess: (BaseMessage message) {
debugPrint("Message pinned successfully: ${message.pinnedAt}");
}, onError: (CometChatException e) {
debugPrint("Message pinning failed with exception: ${e.message}");
});
```

</Tab>
</Tabs>

The call is idempotent — pinning an already-pinned message succeeds and returns the current state.

## Unpin a Message

In order to unpin a message, you can use the `unpinMessage()` method. The returned message carries the pin fields cleared to `null`. The same permission model applies.

<Tabs>
<Tab title="Dart">
```dart
int messageId = 103;

CometChat.unpinMessage(messageId, onSuccess: (BaseMessage message) {
debugPrint("Message unpinned successfully");
}, onError: (CometChatException e) {
debugPrint("Message unpinning failed with exception: ${e.message}");
});
```

</Tab>
</Tabs>

## Real-Time Pin Events

Pin and unpin actions are delivered to all participants through the `MessageListener` class. To receive them, register a listener using the `addMessageListener()` method and override the `onMessagePinned()` and `onMessageUnpinned()` callbacks. Both receive the full updated message object.

<Tabs>
<Tab title="Dart">
```dart
class Class_Name with MessageListener {

//CometChat.addMessageListener("listenerId", this);

@override
void onMessagePinned(BaseMessage message) {
debugPrint("Message pinned: ${message.id} by ${message.pinnedBy}");
}

@override
void onMessageUnpinned(BaseMessage message) {
debugPrint("Message unpinned: ${message.id}");
}
}
```

</Tab>
</Tabs>

The device that performed the action also receives these callbacks on success, so a single code path can update your UI for your own pins and for pins made by other members or your other devices.

## Fetch Pinned Messages

You can fetch all the pinned messages of a conversation by using the `MessagesRequest` class with the `pinned` parameter of the `MessagesRequestBuilder` set to `true`. A pinned list belongs to one conversation, so pair it with the `uid` (for a user conversation) or `guid` (for a group).

<Tabs>
<Tab title="Dart">
```dart
String UID = "cometchat-uid-1";

MessagesRequest messageRequest = (MessagesRequestBuilder()
..uid = UID
..pinned = true
..limit = 50).build();

messageRequest.fetchPrevious(onSuccess: (List<BaseMessage> list) {
debugPrint("Pinned messages fetched: ${list.length}");
}, onError: (CometChatException e) {
debugPrint("Pinned message fetching failed with exception: ${e.message}");
});
```

</Tab>
</Tabs>

## Feature Availability and Limits

Whether the Pin Message feature is enabled for the logged-in user is served on the user's login payload. You can check it at any time using the synchronous `isPinMessageEnabled()` method — it never throws, and returns `true` when the backend did not serve the flag so the feature is not disabled on older backends.

The maximum number of messages that can be pinned per conversation is also served on the login payload and is available through `getPinnedMessagesLimit()`. It returns `null` when the backend did not serve a limit.

<Tabs>
<Tab title="Dart">
```dart
if (CometChat.isPinMessageEnabled()) {
int? limit = CometChat.getPinnedMessagesLimit();
debugPrint("Pinning enabled, limit: ${limit ?? "server default"}");
}
```

</Tab>
</Tabs>

When a pin call exceeds the cap, it fails with the `ERR_PINNED_MESSAGES_LIMIT_EXCEEDED` error code. The exception's `errorParams` map carries the authoritative limit as `{"limit": n}`, which you can interpolate into your error copy.

<Tabs>
<Tab title="Dart">
```dart
CometChat.pinMessage(messageId, onSuccess: (BaseMessage message) {
debugPrint("Message pinned");
}, onError: (CometChatException e) {
if (e.code == 'ERR_PINNED_MESSAGES_LIMIT_EXCEEDED') {
final limit = e.errorParams?['limit'];
debugPrint("You can only pin $limit messages. Unpin one to pin another.");
}
});
```

</Tab>
</Tabs>

<Note>

Pins placed from an admin surface carry the `app_system` sentinel in `pinnedBy`. Save is the private, per-user counterpart of pinning — see [Save Messages](/sdk/flutter/save-messages).

</Note>
119 changes: 119 additions & 0 deletions sdk/flutter/save-messages.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
title: "Save Messages"
description: "Save (bookmark) CometChat messages privately in Flutter apps, sync saves across devices, and fetch the logged-in user's saved messages."
---



Saving bookmarks a message **privately for the logged-in user**. Unlike [pinning](/sdk/flutter/pin-messages), a save is per-viewer: no other member is notified, nothing changes for the rest of the conversation, and any message the user can read — their own or someone else's — can be saved. A saved message carries a `savedAt` timestamp visible only to the user who saved it.

## Save a Message

In order to save a message, you can use the `saveMessage()` method. This method takes the id of the message to be saved. On success it returns the full updated message with `savedAt` stamped.

<Tabs>
<Tab title="Dart">
```dart
int messageId = 103;

CometChat.saveMessage(messageId, onSuccess: (BaseMessage message) {
debugPrint("Message saved successfully: ${message.savedAt}");
}, onError: (CometChatException e) {
debugPrint("Message saving failed with exception: ${e.message}");
});
```

</Tab>
</Tabs>

The call is idempotent — saving an already-saved message succeeds and returns the current state.

## Unsave a Message

In order to unsave a message, you can use the `unsaveMessage()` method. The returned message carries `savedAt` cleared to `null`.

<Tabs>
<Tab title="Dart">
```dart
int messageId = 103;

CometChat.unsaveMessage(messageId, onSuccess: (BaseMessage message) {
debugPrint("Message unsaved successfully");
}, onError: (CometChatException e) {
debugPrint("Message unsaving failed with exception: ${e.message}");
});
```

</Tab>
</Tabs>

## Real-Time Save Events

Because saves are private, save events are delivered only to the **logged-in user's own devices** — the acting device receives the callback on success, and the user's other devices receive it over the socket for cross-device sync. Register a `MessageListener` using the `addMessageListener()` method and override the `onMessageSaved()` and `onMessageUnsaved()` callbacks.

<Tabs>
<Tab title="Dart">
```dart
class Class_Name with MessageListener {

//CometChat.addMessageListener("listenerId", this);

@override
void onMessageSaved(BaseMessage message) {
debugPrint("Message saved: ${message.id}");
}

@override
void onMessageUnsaved(BaseMessage message) {
debugPrint("Message unsaved: ${message.id}");
}
}
```

</Tab>
</Tabs>

## Fetch Saved Messages

You can fetch the logged-in user's saved messages by using the `MessagesRequest` class with the `saved` parameter of the `MessagesRequestBuilder` set to `true`.

<Tabs>
<Tab title="Dart">
```dart
MessagesRequest messageRequest = (MessagesRequestBuilder()
..saved = true
..limit = 50).build();

messageRequest.fetchPrevious(onSuccess: (List<BaseMessage> list) {
debugPrint("Saved messages fetched: ${list.length}");
}, onError: (CometChatException e) {
debugPrint("Saved message fetching failed with exception: ${e.message}");
});
```

</Tab>
</Tabs>

## Feature Availability and Limits

Whether the Save Message feature is enabled for the logged-in user is served on the user's login payload. You can check it at any time using the synchronous `isSaveMessageEnabled()` method — it never throws, and returns `true` when the backend did not serve the flag.

The maximum number of messages a user can save is available through `getSavedMessagesLimit()`, which returns `null` when the backend did not serve a limit.

When a save call exceeds the cap, it fails with the `ERR_SAVED_MESSAGES_LIMIT_EXCEEDED` error code. The exception's `errorParams` map carries the authoritative limit as `{"limit": n}`.

<Tabs>
<Tab title="Dart">
```dart
CometChat.saveMessage(messageId, onSuccess: (BaseMessage message) {
debugPrint("Message saved");
}, onError: (CometChatException e) {
if (e.code == 'ERR_SAVED_MESSAGES_LIMIT_EXCEEDED') {
final limit = e.errorParams?['limit'];
debugPrint("You can only save $limit messages. Unsave one to save another.");
}
});
```

</Tab>
</Tabs>
Loading