markAsRead static method

Future<void> markAsRead(
  1. BaseMessage baseMessage, {
  2. required dynamic onSuccess(
    1. String
    )?,
  3. required dynamic onError(
    1. CometChatException excep
    )?,
})

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.

messageId The ID of the message above which all messages for a particular conversation are to be marked as read.

receiverId In case of one to one conversation message's sender UID will be the receipt's receiver Id.In case of group conversation message's receiver Id will be the receipt's receiver Id

senderId The UID of the sender of the message

Migration Note: Migrated from platform channels to native Dart implementation. Marks the message and all prior messages as read for that conversation.

Uses RealtimeRepository for WebSocket-based receipt delivery. Behavior and signature remain identical for backward compatibility.

Android Reference: CometChat.markAsRead(BaseMessage message, CallbackListener<Void> listener) File: chat-sdk-android/.../CometChat.java:3839

Validation (matches Android):

  • Message must not be null
  • Message sender must not be null
  • User must be logged in
  • Connection must not be feature throttled
  • Settings mode must allow receipts (not LIMITED_TRANSIENT or NO_TRANSIENT)

receiverId Logic (matches Android):

  • For groups: Use message.receiverUid
  • For users (I sent): Use message.receiverUid
  • For users (I received): Use message.sender.uid

Transport (matches Android):

  • If WebSocket connected → send via socket (primary)
  • If WebSocket disconnected/connecting → send via REST API (fallback)

method could throw PlatformException with error codes specifying the cause

Implementation

static Future<void> markAsRead(BaseMessage baseMessage,
    {required Function(String)? onSuccess,
    required Function(CometChatException excep)? onError}) async {
  try {
    // Validation 1: Null sender check (Android behavior)
    // Note: baseMessage itself cannot be null due to Dart null safety
    if (baseMessage.sender == null) {
      final cometChatEx = CometChatException(
        'ERROR_INVALID_MESSAGE',
        'Invalid message',
        'Message sender cannot be null',
      );
      _errorCallbackHandler(cometChatEx, null, null, onError);
      return;
    }

    // Get SDK instance
    final sdk = SdkRegistry.getInstance();

    // Validation 3: User logged in check (Android behavior)
    final loggedInUser = sdk.auth.getLoggedInUser();
    if (loggedInUser == null) {
      final cometChatEx = CometChatException(
        ErrorCode.errorUserNotLoggedIn,
        ErrorMessage.errorMessageUserNotLoggedIn,
        'Please login before marking messages as read',
      );
      _errorCallbackHandler(cometChatEx, null, null, onError);
      return;
    }

    // Validation 4: Connection status check for feature throttled (Android behavior)
    // Note: We still check for feature throttled even when using API fallback
    final connectionStatus = sdk.realtime.connectionState;
    if (connectionStatus == sdk_connection.ConnectionState.featureThrottled) {
      final cometChatEx = CometChatException(
        'ERROR_RECEIPTS_TEMPORARILY_BLOCKED',
        'Receipts temporarily blocked',
        'Mark as read not functional in feature-throttled mode',
      );
      _errorCallbackHandler(cometChatEx, null, null, onError);
      return;
    }

    // Validation 5: Settings mode check (Android behavior)
    /* final settings = await sdk.settings.getCachedSettings();
    if (settings != null &&
        (settings.mode == MODE_LIMITED_TRANSIENT ||
         settings.mode == MODE_NO_TRANSIENT)) {
      throw CometChatException('ERROR_RECEIPTS_TEMPORARILY_BLOCKED', ...);
    }*/

    // Determine receiverId based on Android logic
    // Reference: CometChat.java:3857-3867
    String receiverId;
    if (baseMessage.receiverType == "group") {
      // For groups: always use message's receiverUid
      receiverId = baseMessage.receiverUid;
    } else {
      // For users: check if I sent the message or received it
      if (baseMessage.sender!.uid == loggedInUser.uid) {
        // I sent the message → mark as read for the receiver
        receiverId = baseMessage.receiverUid;
      } else {
        // I received the message → mark as read for the sender
        receiverId = baseMessage.sender!.uid;
      }
    }

    // Check connection status to decide transport method
    // Reference: CometChat.java:4250-4258 - markAsRead(MessageReceipt, CallbackListener)
    // Android: if disconnected/connecting → use API, else use WebSocket
    final isDisconnectedOrConnecting =
        connectionStatus == sdk_connection.ConnectionState.disconnected ||
            connectionStatus == sdk_connection.ConnectionState.connecting;

    if (isDisconnectedOrConnecting) {
      // Use REST API fallback (Android: markAsReadInternal)
      // Reference: CometChat.java:10402-10438
      await sdk.messages.markAsReadViaApi(
        messageId: baseMessage.id,
        receiverId: receiverId,
        receiverType: baseMessage.receiverType,
      );
    } else {
      // Use WebSocket (primary path)
      // Reference: CometChat.java:4255 - rttConnection.markAsRead()
      // Create message receipt (matching Android structure)
      // Reference: CometChat.java:3856-3871
      final receipt = MessageReceipt(
        messageId: baseMessage.id,
        sender: baseMessage.sender!,
        receiverId: receiverId,
        receiverType: baseMessage.receiverType,
        receiptType: 'read', // MessageReceipt.RECEIPT_TYPE_READ
        timestamp: DateTime.now(),
        deliveredAt: baseMessage.deliveredAt,
        readAt: DateTime.now(),
        messageSender: baseMessage.sender!.uid,
      );

      await sdk.realtime.markAsRead(receipt);
    }

    // Call success callback
    if (onSuccess != null) onSuccess('Message marked as read');
  } on SdkException catch (sdkEx) {
    // Convert SdkException to CometChatException
    final cometChatEx = CometChatException(
      sdkEx.code,
      sdkEx.details ?? sdkEx.message,
      sdkEx.message,
    );
    _errorCallbackHandler(cometChatEx, null, null, onError);
  } catch (e) {
    // Handle unexpected exceptions (Android behavior)
    // Reference: CometChat.java:3872-3881
    final cometChatEx = CometChatException(
      ErrorCode.errorUnhandledException,
      e.toString(),
      'Unhandled exception in markAsRead',
    );
    _errorCallbackHandler(cometChatEx, null, null, onError);
  }
}