<commit_before>// @flow

import invariant from 'invariant';
import PropTypes from 'prop-types';
import * as React from 'react';

import type { CalendarQuery } from '../types/entry-types';
import type { AppState } from '../types/redux-types';
import {
  processServerRequestsActionType,
  type ClientClientResponse,
  type ServerRequest,
} from '../types/request-types';
import {
  type RequestsServerSocketMessage,
  type ServerSocketMessage,
  clientSocketMessageTypes,
  serverSocketMessageTypes,
  type ClientSocketMessageWithoutID,
  type SocketListener,
  type ConnectionInfo,
  connectionInfoPropType,
} from '../types/socket-types';
import type { DispatchActionPayload } from '../utils/action-utils';
import { ServerError } from '../utils/errors';
import { connect } from '../utils/redux-utils';
import { InflightRequests, SocketTimeout } from './inflight-requests';

type Props = {|
  inflightRequests: ?InflightRequests,
  sendMessage: (message: ClientSocketMessageWithoutID) => number,
  addListener: (listener: SocketListener) => void,
  removeListener: (listener: SocketListener) => void,
  getClientResponses: (
    activeServerRequests: $ReadOnlyArray<ServerRequest>,
  ) => $ReadOnlyArray<ClientClientResponse>,
  currentCalendarQuery: () => CalendarQuery,
  // Redux state
  connection: ConnectionInfo,
  // Redux dispatch functions
  dispatchActionPayload: DispatchActionPayload,
|};
class RequestResponseHandler extends React.PureComponent<Props> {
  static propTypes = {
    inflightRequests: PropTypes.object,
    sendMessage: PropTypes.func.isRequired,
    addListener: PropTypes.func.isRequired,
    removeListener: PropTypes.func.isRequired,
    getClientResponses: PropTypes.func.isRequired,
    currentCalendarQuery: PropTypes.func.isRequired,
    connection: connectionInfoPropType.isRequired,
    dispatchActionPayload: PropTypes.func.isRequired,
  };

  componentDidMount() {
    this.props.addListener(this.onMessage);
  }

  componentWillUnmount() {
    this.props.removeListener(this.onMessage);
  }

  render() {
    return null;
  }

  onMessage = (message: ServerSocketMessage) => {
    if (message.type !== serverSocketMessageTypes.REQUESTS) {
      return;
    }
    const { serverRequests } = message.payload;
    if (serverRequests.length === 0) {
      return;
    }
    const calendarQuery = this.props.currentCalendarQuery();
    this.props.dispatchActionPayload(processServerRequestsActionType, {
      serverRequests,
      calendarQuery,
    });
    if (this.props.inflightRequests) {
      const clientResponses = this.props.getClientResponses(serverRequests);
      this.sendAndHandleClientResponsesToServerRequests(clientResponses);
    }
  };

  sendClientResponses(
    clientResponses: $ReadOnlyArray<ClientClientResponse>,
  ): Promise<RequestsServerSocketMessage> {
    const { inflightRequests } = this.props;
    invariant(
      inflightRequests,
      'inflightRequests falsey inside sendClientResponses',
    );
    const messageID = this.props.sendMessage({
      type: clientSocketMessageTypes.RESPONSES,
      payload: { clientResponses },
    });
    return inflightRequests.fetchResponse(
      messageID,
      serverSocketMessageTypes.REQUESTS,
    );
  }

  sendAndHandleClientResponsesToServerRequests(
    clientResponses: $ReadOnlyArray<ClientClientResponse>,
  ) {
    if (clientResponses.length === 0) {
      return;
    }
    const promise = this.sendClientResponses(clientResponses);
    this.handleClientResponsesToServerRequests(promise, clientResponses);
  }

  async handleClientResponsesToServerRequests(
    promise: Promise<RequestsServerSocketMessage>,
    clientResponses: $ReadOnlyArray<ClientClientResponse>,
    retriesLeft: number = 1,
  ): Promise<void> {
    try {
      await promise;
    } catch (e) {
      console.log(e);
      if (
        !(e instanceof SocketTimeout) &&
        (!(e instanceof ServerError) || e.message === 'unknown_error') &&
        retriesLeft > 0 &&
        this.props.connection.status === 'connected' &&
        this.props.inflightRequests
      ) {
        // We'll only retry if the connection is healthy and the error is either
        // an unknown_error ServerError or something is neither a ServerError
        // nor a SocketTimeout.
        const newPromise = this.sendClientResponses(clientResponses);
        await this.handleClientResponsesToServerRequests(
          newPromise,
          clientResponses,
          retriesLeft - 1,
        );
      }
    }
  }
}

export default connect(
  (state: AppState) => ({
    connection: state.connection,
  }),
  null,
  true,
)(RequestResponseHandler);
<commit_msg>[lib] Use hook instead of connect function in RequestResponseHandler<commit_after>// @flow

import invariant from 'invariant';
import * as React from 'react';
import { useDispatch } from 'react-redux';

import type { CalendarQuery } from '../types/entry-types';
import type { Dispatch } from '../types/redux-types';
import {
  processServerRequestsActionType,
  type ClientClientResponse,
  type ServerRequest,
} from '../types/request-types';
import {
  type RequestsServerSocketMessage,
  type ServerSocketMessage,
  clientSocketMessageTypes,
  serverSocketMessageTypes,
  type ClientSocketMessageWithoutID,
  type SocketListener,
  type ConnectionInfo,
} from '../types/socket-types';
import { ServerError } from '../utils/errors';
import { useSelector } from '../utils/redux-utils';
import { InflightRequests, SocketTimeout } from './inflight-requests';

type BaseProps = {|
  +inflightRequests: ?InflightRequests,
  +sendMessage: (message: ClientSocketMessageWithoutID) => number,
  +addListener: (listener: SocketListener) => void,
  +removeListener: (listener: SocketListener) => void,
  +getClientResponses: (
    activeServerRequests: $ReadOnlyArray<ServerRequest>,
  ) => $ReadOnlyArray<ClientClientResponse>,
  +currentCalendarQuery: () => CalendarQuery,
|};
type Props = {|
  ...BaseProps,
  +connection: ConnectionInfo,
  +dispatch: Dispatch,
|};
class RequestResponseHandler extends React.PureComponent<Props> {
  componentDidMount() {
    this.props.addListener(this.onMessage);
  }

  componentWillUnmount() {
    this.props.removeListener(this.onMessage);
  }

  render() {
    return null;
  }

  onMessage = (message: ServerSocketMessage) => {
    if (message.type !== serverSocketMessageTypes.REQUESTS) {
      return;
    }
    const { serverRequests } = message.payload;
    if (serverRequests.length === 0) {
      return;
    }
    const calendarQuery = this.props.currentCalendarQuery();
    this.props.dispatch({
      type: processServerRequestsActionType,
      payload: {
        serverRequests,
        calendarQuery,
      },
    });
    if (this.props.inflightRequests) {
      const clientResponses = this.props.getClientResponses(serverRequests);
      this.sendAndHandleClientResponsesToServerRequests(clientResponses);
    }
  };

  sendClientResponses(
    clientResponses: $ReadOnlyArray<ClientClientResponse>,
  ): Promise<RequestsServerSocketMessage> {
    const { inflightRequests } = this.props;
    invariant(
      inflightRequests,
      'inflightRequests falsey inside sendClientResponses',
    );
    const messageID = this.props.sendMessage({
      type: clientSocketMessageTypes.RESPONSES,
      payload: { clientResponses },
    });
    return inflightRequests.fetchResponse(
      messageID,
      serverSocketMessageTypes.REQUESTS,
    );
  }

  sendAndHandleClientResponsesToServerRequests(
    clientResponses: $ReadOnlyArray<ClientClientResponse>,
  ) {
    if (clientResponses.length === 0) {
      return;
    }
    const promise = this.sendClientResponses(clientResponses);
    this.handleClientResponsesToServerRequests(promise, clientResponses);
  }

  async handleClientResponsesToServerRequests(
    promise: Promise<RequestsServerSocketMessage>,
    clientResponses: $ReadOnlyArray<ClientClientResponse>,
    retriesLeft: number = 1,
  ): Promise<void> {
    try {
      await promise;
    } catch (e) {
      console.log(e);
      if (
        !(e instanceof SocketTimeout) &&
        (!(e instanceof ServerError) || e.message === 'unknown_error') &&
        retriesLeft > 0 &&
        this.props.connection.status === 'connected' &&
        this.props.inflightRequests
      ) {
        // We'll only retry if the connection is healthy and the error is either
        // an unknown_error ServerError or something is neither a ServerError
        // nor a SocketTimeout.
        const newPromise = this.sendClientResponses(clientResponses);
        await this.handleClientResponsesToServerRequests(
          newPromise,
          clientResponses,
          retriesLeft - 1,
        );
      }
    }
  }
}

export default React.memo<BaseProps>(function ConnectedRequestResponseHandler(
  props: BaseProps,
) {
  const connection = useSelector((state) => state.connection);
  const dispatch = useDispatch();

  return (
    <RequestResponseHandler
      {...props}
      connection={connection}
      dispatch={dispatch}
    />
  );
});

{'fraction_non_alphanumeric': 0.10551930096228293, 'fraction_numerical': 0.0011060723371308485, 'mean_word_length': 3.6801242236024843, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 39, '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': '3390712', 'n_tokens_mistral': 2548, 'n_tokens_neox': 2581, 'n_words': 519}