forceScrollToFirstMessageInChain method

void forceScrollToFirstMessageInChain(
  1. String responseId
)

Find first message in a response chain and force scroll to it with extra reliability

Implementation

void forceScrollToFirstMessageInChain(String responseId) {
  if (_scrollController?.hasClients != true) return;

  // Implement debounce for scrolling to prevent jitter
  final now = DateTime.now();
  if (now.difference(_lastScrollTime).inMilliseconds < _scrollDebounceMs) {
    debugPrint('SCROLL DEBOUNCED: Too soon after last scroll');
    return;
  }
  _lastScrollTime = now;

  // Log information about the animation being used for debugging
  final animationInfo =
      'Animation: duration=${scrollBehaviorConfig.scrollAnimationDuration.inMilliseconds}ms, '
      'curve=${scrollBehaviorConfig.scrollAnimationCurve.runtimeType}';
  debugPrint('SCROLL ANIMATION INFO: $animationInfo');

  try {
    // Find the first message with this responseId
    final firstMessageInChain = _messages.firstWhere(
      (msg) =>
          msg.customProperties?['responseId'] == responseId &&
          (msg.customProperties?['isStartOfResponse'] == true ||
              msg.customProperties?['isFirstResponseMessage'] == true),
      orElse: () => _messages.firstWhere(
        (msg) => msg.customProperties?['responseId'] == responseId,
        orElse: () => throw Exception('No message found with responseId: $responseId'),
      ),
    );

    // Find the index of this message
    final index = _messages.indexOf(firstMessageInChain);
    if (index < 0) {
      debugPrint('FORCE SCROLL: Message not found in list');
      return;
    }

    debugPrint(
        'FORCE SCROLL: Found first message in chain at index $index with responseId: $responseId, reverseOrder: ${paginationConfig.reverseOrder}');

    // Always use animation when testing different animation curves
    final scrollDuration = scrollBehaviorConfig.scrollAnimationDuration;
    final scrollCurve = scrollBehaviorConfig.scrollAnimationCurve;

    debugPrint(
        'APPLYING ANIMATION: duration=${scrollDuration.inMilliseconds}ms, curve=$scrollCurve');

    // Use a simple approach: scroll to a calculated position based on message index
    // Get list properties
    final maxExtent = _scrollController!.position.maxScrollExtent;
    final itemCount = _messages.length;

    debugPrint('SCROLL INFO: maxExtent=$maxExtent, itemCount=$itemCount, messageIndex=$index');

    double targetPosition;

    if (paginationConfig.reverseOrder) {
      // In reverse order mode (newest messages at bottom)
      // Index 0 = newest message (bottom), higher index = older messages (top)
      // We want to show the first message of the response, so scroll towards the top
      if (index == 0) {
        targetPosition = 0.0; // Show the newest message (at bottom)
      } else {
        // Calculate position to show this message near the top of the viewport
        // Since it's reverse order, we need to scroll down more to see older messages
        targetPosition = maxExtent * (index / itemCount) * 0.8; // Show near top of viewport
      }
    } else {
      // In chronological mode (oldest messages at top)
      // Index 0 = oldest message (top), higher index = newer messages (bottom)
      // We want to show the first message of the response near the top
      if (index < itemCount * 0.2) {
        // If message is in first 20% of list, scroll to top
        targetPosition = 0.0;
      } else {
        // Calculate position to show this message near the top of viewport
        targetPosition = (maxExtent * (index / itemCount)) - (maxExtent * 0.2);
      }
    }

    // Clamp to valid range
    targetPosition = targetPosition.clamp(0.0, maxExtent);

    debugPrint(
        'FORCE SCROLL: Scrolling to position $targetPosition (reverse: ${paginationConfig.reverseOrder})');

    _scrollController!.animateTo(
      targetPosition,
      duration: scrollDuration,
      curve: scrollCurve,
    );

    debugPrint(
        'FORCE SCROLL: Animation started to first message in chain using ${scrollCurve.runtimeType}');
  } catch (e) {
    debugPrint('ERROR FORCE SCROLLING TO CHAIN: $e');
    // Fallback: just scroll to top to show the beginning of messages
    try {
      _scrollController!.animateTo(
        0.0,
        duration: scrollBehaviorConfig.scrollAnimationDuration,
        curve: scrollBehaviorConfig.scrollAnimationCurve,
      );
      debugPrint('FALLBACK SCROLL: Scrolled to top as fallback');
    } catch (fallbackError) {
      debugPrint('FALLBACK SCROLL ERROR: $fallbackError');
    }
  }
}