<commit_before>import React, {Fragment} from 'react';
import PropTypes from 'prop-types';
import {graphql, createPaginationContainer} from 'react-relay';

import {PAGE_SIZE, PAGINATION_WAIT_TIME_MS} from '../../helpers';
import {RelayConnectionPropType} from '../../prop-types';
import Accumulator from './accumulator';
import ReviewCommentsAccumulator from './review-comments-accumulator';

class ReviewThreadsAccumulator extends React.Component {
  static propTypes = {
    // Relay props
    relay: PropTypes.shape({
      hasMore: PropTypes.func.isRequired,
      loadMore: PropTypes.func.isRequired,
      isLoading: PropTypes.func.isRequired,
    }).isRequired,
    pullRequest: PropTypes.shape({
      reviewThreads: RelayConnectionPropType(
        PropTypes.object,
      ),
    }),

    // Render prop. Called with (array of errors, map of threads to commits, loading)
    children: PropTypes.func,

    // Non-render prop. Called with (array of errors, map of threads to commits, loading)
    handleResults: PropTypes.func,
  }

  constructor(props) {
    super(props);

    this.state = {
      errors: [],
      commentsByThread: new Map(),
      loadingByThread: new Map(),
    };
  }

  render() {
    const resultBatch = this.props.pullRequest.reviewThreads.edges.map(edge => edge.node);

    return (
      <Fragment>
        <Accumulator
          relay={this.props.relay}
          resultBatch={resultBatch}
          handleResults={this.handleThreadResults}
          pageSize={PAGE_SIZE}
          waitTimeMs={PAGINATION_WAIT_TIME_MS}>
          {this.renderReviewThreads}
        </Accumulator>
        {this.props.children(this.state.errors, this.state.commentsByThread, this.anyLoading())}
      </Fragment>
    );
  }

  renderReviewThreads = (err, threads) => {
    if (err) {
      return null;
    }

    return threads.map(thread => (
      <ReviewCommentsAccumulator
        key={`review-comments-${thread.id}`}
        reviewThread={thread}
        handleResults={(subError, comments, loading) => this.handleCommentResults(subError, thread, comments, loading)}
      />
    ));
  }

  handleThreadResults = (err, _threads, _loading) => {
    if (err) {
      this.setState(prevState => ({errors: [...prevState.errors, err]}));
    }
  }

  handleCommentResults(err, thread, comments, loading) {
    this.setState(prevState => {
      prevState.loadingByThread.set(thread, loading);
      prevState.commentsByThread.set(thread, comments);

      return {errors: [...prevState.errors, err]};
    });
  }

  anyLoading() {
    return this.props.relay.hasMore() || Array.from(this.state.loadingByThread.values()).some(Boolean);
  }
}

export default createPaginationContainer(ReviewThreadsAccumulator, {
  pullRequest: graphql`
    fragment reviewThreadsAccumulator_pullRequest on PullRequest
    @argumentDefinitions(
      threadCount: {type: "Int!"}
      threadCursor: {type: "String"}<commit_msg>Call handleResults() from ReviewThreadsAccumulator<commit_after>import React, {Fragment} from 'react';
import PropTypes from 'prop-types';
import {graphql, createPaginationContainer} from 'react-relay';

import {PAGE_SIZE, PAGINATION_WAIT_TIME_MS} from '../../helpers';
import {RelayConnectionPropType} from '../../prop-types';
import Accumulator from './accumulator';
import ReviewCommentsAccumulator from './review-comments-accumulator';

class ReviewThreadsAccumulator extends React.Component {
  static propTypes = {
    // Relay props
    relay: PropTypes.shape({
      hasMore: PropTypes.func.isRequired,
      loadMore: PropTypes.func.isRequired,
      isLoading: PropTypes.func.isRequired,
    }).isRequired,
    pullRequest: PropTypes.shape({
      reviewThreads: RelayConnectionPropType(
        PropTypes.object,
      ),
    }),

    // Render prop. Called with (array of errors, array of threads, map of comments per thread, loading)
    children: PropTypes.func,

    // Non-render prop. Called with (array of errors, array of threads, map of comments per thread, loading)
    handleResults: PropTypes.func,
  }

  static defaultProps = {
    children: () => null,
    handleResults: () => {},
  }

  constructor(props) {
    super(props);

    this.state = {
      errors: [],
      threads: [],
      commentsByThread: new Map(),
      loadingByThread: new Map(),
    };
  }

  render() {
    const resultBatch = this.props.pullRequest.reviewThreads.edges.map(edge => edge.node);

    return (
      <Fragment>
        <Accumulator
          relay={this.props.relay}
          resultBatch={resultBatch}
          handleResults={this.handleThreadResults}
          pageSize={PAGE_SIZE}
          waitTimeMs={PAGINATION_WAIT_TIME_MS}>
          {this.renderReviewThreads}
        </Accumulator>
        {this.props.children(this.state.errors, this.state.threads, this.state.commentsByThread, this.anyLoading())}
      </Fragment>
    );
  }

  renderReviewThreads = (err, threads) => {
    if (err) {
      return null;
    }

    return threads.map(thread => (
      <ReviewCommentsAccumulator
        key={`review-comments-${thread.id}`}
        reviewThread={thread}
        handleResults={(subError, comments, loading) => this.handleCommentResults(subError, thread, comments, loading)}
      />
    ));
  }

  handleThreadResults = (err, threads) => {
    this.setState(prevState => {
      for (const thread of threads) {
        if (!prevState.commentsByThread.has(thread)) {
          prevState.commentsByThread.set(thread, []);
        }
        if (!prevState.loadingByThread.has(thread)) {
          prevState.loadingByThread.set(thread, true);
        }
      }

      return {
        errors: err !== null ? [...prevState.errors, err] : prevState.errors,
        threads,
      };
    }, () => {
      this.props.handleResults(this.state.errors, this.state.threads, this.state.commentsByThread, this.anyLoading());
    });
  }

  handleCommentResults(err, thread, comments, loading) {
    this.setState(prevState => {
      prevState.loadingByThread.set(thread, loading);
      prevState.commentsByThread.set(thread, comments);

      return {errors: err !== null ? [...prevState.errors, err] : prevState.errors};
    }, () => {
      this.props.handleResults(this.state.errors, this.state.threads, this.state.commentsByThread, this.anyLoading());
    });
  }

  anyLoading() {
    return this.props.relay.hasMore() ||
      this.state.loadingReviews ||
      Array.from(this.state.loadingByThread.values()).some(Boolean);
  }
}

export default createPaginationContainer(ReviewThreadsAccumulator, {
  pullRequest: graphql`
    fragment reviewThreadsAccumulator_pullRequest on PullRequest
    @argumentDefinitions(
      threadCount: {type: "Int!"}
      threadCursor: {type: "String"}
{'fraction_non_alphanumeric': 0.12911843276936777, 'fraction_numerical': 0.0, 'mean_word_length': 3.134355828220859, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7586899', 'n_tokens_mistral': 1967, 'n_tokens_neox': 1969, 'n_words': 389}