fetchPrevious method

Future<List<BaseMessage>> fetchPrevious({
  1. required dynamic onSuccess(
    1. List<BaseMessage> message
    )?,
  2. required dynamic onError(
    1. CometChatException excep
    )?,
})

Returns a list of BaseMessage object fetched after putting the filters.

Android Reference: MessagesRequest.fetchPrevious() Migration Note: Migrated from platform channels to native Dart. Uses MessageRepository for backend communication. Fetches older messages (prepend direction).

Implementation

Future<List<BaseMessage>> fetchPrevious(
    {required Function(List<BaseMessage> message)? onSuccess,
    required Function(CometChatException excep)? onError}) async {
  try {
    // 1. Validate request parameters
    // **Android Reference:** MessagesRequest.fetchPrevious() → validateMessageRequest()
    final validationError = _validateMessageRequest();
    if (validationError != null) {
      if (onError != null) onError(validationError);
      return [];
    }

    // 2. Check updatesOnly without updatedAfter
    // **Android Reference:** if (updatesOnly && updatedAfter == -1)
    final int? updatedAfterLong = updatedAfter != null
        ? updatedAfter!.millisecondsSinceEpoch ~/ 1000
        : null;
    if ((updatesOnly ?? false) && updatedAfterLong == null) {
      final error = CometChatException(
        'ERR_UPDATESONLY_WITHOUT_UPDATEDAFTER',
        'updatedAfter must be set when updatesOnly is true',
        'updatedAfter must be set when updatesOnly is true',
      );
      if (onError != null) onError(error);
      return [];
    }

    // 3. Check hasPrevious flag — return empty list if no more messages
    // **Android Reference:** MessagesRequest.fetchPrevious() hasPrevious check
    if (!_hasPrevious) {
      if (onSuccess != null) onSuccess([]);
      return [];
    }

    // 4. Check limit does not exceed MAX_LIMIT
    // **Android Reference:** Each fetch method checks limit <= MAX_LIMIT
    if ((limit ?? 0) > (maxLimit ?? 100)) {
      final error = CometChatException(
        'ERR_LIMIT_EXCEEDED',
        'Limit cannot exceed $maxLimit',
        'Limit cannot exceed $maxLimit',
      );
      if (onError != null) onError(error);
      return [];
    }

    // 5. Check inProgress flag — return error if already fetching
    // **Android Reference:** returns ERROR_REQUEST_IN_PROGRESS to onError
    if (_inProgress) {
      final error = CometChatException(
        'ERR_REQUEST_IN_PROGRESS',
        'A fetch request is already in progress',
        'A fetch request is already in progress',
      );
      if (onError != null) onError(error);
      return [];
    }

    // Set inProgress flag before fetch
    // **Android Reference:** MessagesRequest.fetchPrevious() line: inProgress = true
    _inProgress = true;

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

    // 2. Get updatedAfter as Unix timestamp (already computed above)
    int? updateAfterLong = updatedAfterLong;

    // 3. Call repository based on parentMessageId/uid/guid
    // **Android Reference:** MessagesRequest.fetchPrevious() routing:
    //   parentMessageId first, then uid+guid, then uid, then guid, then fallback
    late final result;

    if (parentMessageId != null && parentMessageId != 0) {
      // Threaded messages — checked FIRST per Android SDK
      result = await sdk.messages.getThreadedMessages(
        parentMessageId.toString(),
        limit: limit ?? 30,
        affix: 'prepend', // fetchPrevious = prepend (older messages)
        timestamp: _timestamp,
        messageId: _messageId?.toString(),
        unread: unread,
        hideMessagesFromBlockedUsers: hideMessagesFromBlockedUsers,
        searchKeyword: searchKeyword,
        updatedAfter: updateAfterLong,
        updatesOnly: updatesOnly,
        categories: categories,
        types: types,
        hideDeleted: hideDeleted,
        tags: tags,
        withTags: withTags,
        interactionGoalCompleted: interactionGoalCompletedOnly,
        mentionsWithTagInfo: _mentionsWithTagInfo,
        mentionsWithBlockedInfo: _mentionsWithBlockedInfo,
        hasAttachments: hasAttachments,
        hasLinks: hasLinks,
        hasMentions: hasMentions,
        hasReactions: hasReactions,
        mentionedUids: mentionedUids,
        attachmentTypes: attachmentTypes,
        withParent: withParent,
        hideQuotedMessages: hideQuotedMessages,
      );
    } else if (uid != null &&
        uid!.isNotEmpty &&
        guid != null &&
        guid!.isNotEmpty) {
      // User messages in a specific group
      // **Android Reference:** fetchMessagesForUserInGroup()
      result = await sdk.messages.getUserMessages(
        uid!,
        limit: limit ?? 30,
        affix: 'prepend',
        timestamp: _timestamp,
        messageId: _messageId?.toString(),
        unread: unread,
        hideMessagesFromBlockedUsers: hideMessagesFromBlockedUsers,
        searchKeyword: searchKeyword,
        updatedAfter: updateAfterLong,
        updatesOnly: updatesOnly,
        categories: categories,
        types: types,
        hideReplies: hideReplies,
        hideDeleted: hideDeleted,
        tags: tags,
        withTags: withTags,
        interactionGoalCompleted: interactionGoalCompletedOnly,
        mentionsWithTagInfo: _mentionsWithTagInfo,
        mentionsWithBlockedInfo: _mentionsWithBlockedInfo,
        hasAttachments: hasAttachments,
        hasLinks: hasLinks,
        hasMentions: hasMentions,
        hasReactions: hasReactions,
        mentionedUids: mentionedUids,
        attachmentTypes: attachmentTypes,
        hideQuotedMessages: hideQuotedMessages,
      );
    } else if (uid != null && uid!.isNotEmpty) {
      // User messages
      result = await sdk.messages.getUserMessages(
        uid!,
        limit: limit ?? 30,
        affix: 'prepend', // fetchPrevious = prepend (older messages)
        timestamp: _timestamp,
        messageId: _messageId?.toString(),
        unread: unread,
        hideMessagesFromBlockedUsers: hideMessagesFromBlockedUsers,
        searchKeyword: searchKeyword,
        updatedAfter: updateAfterLong,
        updatesOnly: updatesOnly,
        categories: categories,
        types: types,
        hideReplies: hideReplies,
        hideDeleted: hideDeleted,
        tags: tags,
        withTags: withTags,
        interactionGoalCompleted: interactionGoalCompletedOnly,
        mentionsWithTagInfo: _mentionsWithTagInfo,
        mentionsWithBlockedInfo: _mentionsWithBlockedInfo,
        hasAttachments: hasAttachments,
        hasLinks: hasLinks,
        hasMentions: hasMentions,
        hasReactions: hasReactions,
        mentionedUids: mentionedUids,
        attachmentTypes: attachmentTypes,
        hideQuotedMessages: hideQuotedMessages,
      );
    } else if (guid != null && guid!.isNotEmpty) {
      // Group messages
      result = await sdk.messages.getGroupMessages(
        guid!,
        limit: limit ?? 30,
        affix: 'prepend', // fetchPrevious = prepend (older messages)
        timestamp: _timestamp,
        messageId: _messageId?.toString(),
        unread: unread,
        hideMessagesFromBlockedUsers: hideMessagesFromBlockedUsers,
        searchKeyword: searchKeyword,
        updatedAfter: updateAfterLong,
        updatesOnly: updatesOnly,
        categories: categories,
        types: types,
        hideReplies: hideReplies,
        hideDeleted: hideDeleted,
        tags: tags,
        withTags: withTags,
        interactionGoalCompleted: interactionGoalCompletedOnly,
        mentionsWithTagInfo: _mentionsWithTagInfo,
        mentionsWithBlockedInfo: _mentionsWithBlockedInfo,
        hasAttachments: hasAttachments,
        hasLinks: hasLinks,
        hasMentions: hasMentions,
        hasReactions: hasReactions,
        mentionedUids: mentionedUids,
        attachmentTypes: attachmentTypes,
        hideQuotedMessages: hideQuotedMessages,
      );
    } else {
      // All messages
      result = await sdk.messages.getMessages(
        limit: limit ?? 30,
        timestamp: _timestamp,
        messageId: _messageId?.toString(),
        affix: 'prepend', // fetchPrevious = prepend (older messages)
        unread: unread,
        hideMessagesFromBlockedUsers: hideMessagesFromBlockedUsers,
        searchKeyword: searchKeyword,
        updatedAfter: updateAfterLong,
        updatesOnly: updatesOnly,
        categories: categories,
        types: types,
        hideReplies: hideReplies,
        hideDeleted: hideDeleted,
        tags: tags,
        withTags: withTags,
        interactionGoalCompleted: interactionGoalCompletedOnly,
        mentionsWithTagInfo: _mentionsWithTagInfo,
        mentionsWithBlockedInfo: _mentionsWithBlockedInfo,
        hasAttachments: hasAttachments,
        hasLinks: hasLinks,
        hasMentions: hasMentions,
        hasReactions: hasReactions,
        mentionedUids: mentionedUids,
        attachmentTypes: attachmentTypes,
        hideQuotedMessages: hideQuotedMessages,
      );
    }

    // 4. Update pagination cursor for next fetch
    // For prepend (fetchPrevious), we update to the MIN messageId/timestamp
    // Reference: Android SDK updateMessageIdAndTimestamp() method
    _updatePaginationCursor(result.messages, isAppend: false);

    // 5. Update pagination state from response
    // **Android Reference:** MessagesRequest.fetchPrevious() pagination state update
    _updatePaginationState(result);

    // 6. Update hasNext/hasPrevious flags based on page comparison
    // **Android Reference:** if (affix==PREPEND && currentPage==totalPages) { hasPrevious=false; hasNext=true; }
    if (_currentPage != -1 &&
        _totalPages != -1 &&
        _currentPage == _totalPages) {
      _hasPrevious = false;
      _hasNext = true;
    }
    // Also set hasPrevious=false if no messages returned (safety net)
    if (result.messages.isEmpty) {
      _hasPrevious = false;
    }

    // 7. Reset inProgress flag after successful fetch
    // **Android Reference:** MessagesRequest.fetchPrevious() line: inProgress = false
    _inProgress = false;

    // 8. Call callbacks
    if (onSuccess != null) {
      onSuccess(result.messages);
    }
    return result.messages;
  } on sdk_errors.SdkException catch (sdkEx) {
    // Reset inProgress flag on error
    _inProgress = false;
    // Convert SdkException to CometChatException
    final cometChatEx = CometChatException(
      sdkEx.code,
      sdkEx.details ?? sdkEx.message,
      sdkEx.message,
    );
    if (onError != null) {
      onError(cometChatEx);
    }
  } catch (e) {
    // Reset inProgress flag on error
    _inProgress = false;
    debugPrint("Error: ${e.toString()}");
    // Handle unexpected errors
    final cometChatEx = CometChatException(
      ErrorCode.errorUnhandledException,
      e.toString(),
      e.toString(),
    );
    if (onError != null) {
      onError(cometChatEx);
    }
  }
  return [];
}