<commit_before>// @flow

import type { ViewStyle } from '../types/styles';
import type {
  NativeMethodsMixinType,
} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';

import * as React from 'react';
import PropTypes from 'prop-types';
import {
  View,
  Image,
  StyleSheet,
  InteractionManager,
  I18nManager,
  PanResponder,
  ViewPropTypes,
  Text,
  Platform,
  Keyboard,
} from 'react-native';
import tinycolor from 'tinycolor2';
import invariant from 'invariant';

import Button from './button.react';

type PanEvent = {
  nativeEvent: {
    pageX: number,
    pageY: number,
  },
};
type HSVColor = {| h: number, s: number, v: number |};
type PickerContainer = React.ElementRef<typeof View> & NativeMethodsMixinType;
type Props = {|
  color?: string | HSVColor,
  defaultColor?: string,
  oldColor?: ?string,
  onColorChange?: (color: HSVColor) => void,
  onColorSelected?: (color: string) => void,
  onOldColorSelected?: (color: string) => void,
  style?: ViewStyle,
  buttonText: string,
  oldButtonText: string,
|};
type State = {|
  color: HSVColor,
  pickerSize: ?number,
|};
class ColorPicker extends React.PureComponent<Props, State> {

  static propTypes = {
    color: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.shape({
        h: PropTypes.number,
        s: PropTypes.number,
        v: PropTypes.number,
      }),
    ]),
    defaultColor: PropTypes.string,
    oldColor: PropTypes.string,
    onColorChange: PropTypes.func,
    onColorSelected: PropTypes.func,
    onOldColorSelected: PropTypes.func,
    style: ViewPropTypes.style,
    buttonText: PropTypes.string,
    oldButtonText: PropTypes.string,
  };
  static defaultProps = {
    buttonText: "Select",
    oldButtonText: "Reset",
  };
  _layout = { width: 0, height: 0 };
  _pageX = 0;
  _pageY = 0;
  _pickerContainer: ?PickerContainer = null;
  _pickerResponder: ?PanResponder = null;
  _changingHColor = false;

  constructor(props: Props) {
    super(props);
    let color;
    if (props.defaultColor) {
      color = tinycolor(props.defaultColor).toHsv();
    } else if (props.oldColor) {
      color = tinycolor(props.oldColor).toHsv();
    } else {
      color = { h: 0, s: 1, v: 1 };
    }
    this.state = { color, pickerSize: null };
  }

  componentWillMount() {
    Keyboard.dismiss();
    const handleColorChange = ({ x, y }: { x: number, y: number }) => {
      if (this._changingHColor) {
        this._handleHColorChange({ x, y });
      } else {
        this._handleSVColorChange({ x, y });
      }
    };
    this._pickerResponder = PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onStartShouldSetPanResponderCapture: () => true,
      onMoveShouldSetPanResponder: () => true,
      onMoveShouldSetPanResponderCapture: () => true,
      onPanResponderTerminationRequest: () => true,
      onPanResponderGrant: (evt: PanEvent) => {
        const x = evt.nativeEvent.pageX;
        const y = evt.nativeEvent.pageY;
        const { s, v } = this._computeColorFromTriangle({ x, y });
        this._changingHColor = s > 1 || s < 0 || v > 1 || v < 0;
        handleColorChange({ x, y });
      },
      onPanResponderMove: (evt: PanEvent) => handleColorChange({
        x: evt.nativeEvent.pageX,
        y: evt.nativeEvent.pageY,
      }),
      onPanResponderRelease: () => true,
    });
  }

  _getColor(): HSVColor {
    const passedColor = typeof this.props.color === 'string'
      ? tinycolor(this.props.color).toHsv()
      : this.props.color;
    return passedColor || this.state.color;
  }

  _onColorSelected = () => {
    const { onColorSelected } = this.props;
    const color = tinycolor(this._getColor()).toHexString();
    onColorSelected && onColorSelected(color);
  }

  _onOldColorSelected = () => {
    const { oldColor, onOldColorSelected } = this.props;
    const color = tinycolor(oldColor);
    this.setState({ color: color.toHsv() });
    onOldColorSelected && onOldColorSelected(color.toHexString());
  }

  _onSValueChange = (s: number) => {
    const { h, v } = this._getColor();
    this._onColorChange({ h, s, v });
  }

  _onVValueChange = (v: number) => {
    const { h, s } = this._getColor();
    this._onColorChange({ h, s, v });
  }

  _onColorChange(color: HSVColor) {
    this.setState({ color });
    if (this.props.onColorChange) {
      this.props.onColorChange(color);
    }
  }

  _onLayout = (l: { nativeEvent: { layout: {
    width: number,
    height: number,
  }}}) => {
    this._layout = l.nativeEvent.layout;
    const { width, height } = this._layout;
    const pickerSize = Math.round(Math.min(width, height));
    if (
      !this.state.pickerSize ||
      Math.abs(this.state.pickerSize - pickerSize) >= 3
    ) {
      this.setState({ pickerSize });
    }

    // We need to get pageX/pageY, ie. the absolute position of the picker on
    // the screen. This is because PanResponder's relative position information
    // is double broken (#12591, #15290). Unfortunately, the only way to get
    // absolute positioning for a View is via measure() after onLayout (#10556).
    InteractionManager.runAfterInteractions(() => {
      invariant(this._pickerContainer, "should be set");
      this._pickerContainer.measure((x, y, width, height, pageX, pageY) => {
        this._pageX = pageX;
        this._pageY = pageY;
      });
    });
  }

  _computeHValue(x: number, y: number) {
    const pickerSize = this.state.pickerSize;
    invariant(
      pickerSize !== null && pickerSize !== undefined,
      "pickerSize should be set",
    );
    const dx = x - pickerSize / 2;
    const dy = y - pickerSize / 2;
    const rad = Math.atan2(dx, dy) + Math.PI + Math.PI / 2;
    return rad * 180 / Math.PI % 360;
  }

  _hValueToRad(deg: number) {
    const rad = deg * Math.PI / 180;<commit_msg>[native] Fix ColorPicker crash on unmount<commit_after>// @flow

import type { ViewStyle } from '../types/styles';
import type {
  NativeMethodsMixinType,
} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';

import * as React from 'react';
import PropTypes from 'prop-types';
import {
  View,
  Image,
  StyleSheet,
  InteractionManager,
  I18nManager,
  PanResponder,
  ViewPropTypes,
  Text,
  Platform,
  Keyboard,
} from 'react-native';
import tinycolor from 'tinycolor2';
import invariant from 'invariant';

import Button from './button.react';

type PanEvent = {
  nativeEvent: {
    pageX: number,
    pageY: number,
  },
};
type HSVColor = {| h: number, s: number, v: number |};
type PickerContainer = React.ElementRef<typeof View> & NativeMethodsMixinType;
type Props = {|
  color?: string | HSVColor,
  defaultColor?: string,
  oldColor?: ?string,
  onColorChange?: (color: HSVColor) => void,
  onColorSelected?: (color: string) => void,
  onOldColorSelected?: (color: string) => void,
  style?: ViewStyle,
  buttonText: string,
  oldButtonText: string,
|};
type State = {|
  color: HSVColor,
  pickerSize: ?number,
|};
class ColorPicker extends React.PureComponent<Props, State> {

  static propTypes = {
    color: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.shape({
        h: PropTypes.number,
        s: PropTypes.number,
        v: PropTypes.number,
      }),
    ]),
    defaultColor: PropTypes.string,
    oldColor: PropTypes.string,
    onColorChange: PropTypes.func,
    onColorSelected: PropTypes.func,
    onOldColorSelected: PropTypes.func,
    style: ViewPropTypes.style,
    buttonText: PropTypes.string,
    oldButtonText: PropTypes.string,
  };
  static defaultProps = {
    buttonText: "Select",
    oldButtonText: "Reset",
  };
  _layout = { width: 0, height: 0 };
  _pageX = 0;
  _pageY = 0;
  _pickerContainer: ?PickerContainer = null;
  _pickerResponder: ?PanResponder = null;
  _changingHColor = false;

  constructor(props: Props) {
    super(props);
    let color;
    if (props.defaultColor) {
      color = tinycolor(props.defaultColor).toHsv();
    } else if (props.oldColor) {
      color = tinycolor(props.oldColor).toHsv();
    } else {
      color = { h: 0, s: 1, v: 1 };
    }
    this.state = { color, pickerSize: null };
  }

  componentWillMount() {
    Keyboard.dismiss();
    const handleColorChange = ({ x, y }: { x: number, y: number }) => {
      if (this._changingHColor) {
        this._handleHColorChange({ x, y });
      } else {
        this._handleSVColorChange({ x, y });
      }
    };
    this._pickerResponder = PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onStartShouldSetPanResponderCapture: () => true,
      onMoveShouldSetPanResponder: () => true,
      onMoveShouldSetPanResponderCapture: () => true,
      onPanResponderTerminationRequest: () => true,
      onPanResponderGrant: (evt: PanEvent) => {
        const x = evt.nativeEvent.pageX;
        const y = evt.nativeEvent.pageY;
        const { s, v } = this._computeColorFromTriangle({ x, y });
        this._changingHColor = s > 1 || s < 0 || v > 1 || v < 0;
        handleColorChange({ x, y });
      },
      onPanResponderMove: (evt: PanEvent) => handleColorChange({
        x: evt.nativeEvent.pageX,
        y: evt.nativeEvent.pageY,
      }),
      onPanResponderRelease: () => true,
    });
  }

  _getColor(): HSVColor {
    const passedColor = typeof this.props.color === 'string'
      ? tinycolor(this.props.color).toHsv()
      : this.props.color;
    return passedColor || this.state.color;
  }

  _onColorSelected = () => {
    const { onColorSelected } = this.props;
    const color = tinycolor(this._getColor()).toHexString();
    onColorSelected && onColorSelected(color);
  }

  _onOldColorSelected = () => {
    const { oldColor, onOldColorSelected } = this.props;
    const color = tinycolor(oldColor);
    this.setState({ color: color.toHsv() });
    onOldColorSelected && onOldColorSelected(color.toHexString());
  }

  _onSValueChange = (s: number) => {
    const { h, v } = this._getColor();
    this._onColorChange({ h, s, v });
  }

  _onVValueChange = (v: number) => {
    const { h, s } = this._getColor();
    this._onColorChange({ h, s, v });
  }

  _onColorChange(color: HSVColor) {
    this.setState({ color });
    if (this.props.onColorChange) {
      this.props.onColorChange(color);
    }
  }

  _onLayout = (l: { nativeEvent: { layout: {
    width: number,
    height: number,
  }}}) => {
    this._layout = l.nativeEvent.layout;
    const { width, height } = this._layout;
    const pickerSize = Math.round(Math.min(width, height));
    if (
      !this.state.pickerSize ||
      Math.abs(this.state.pickerSize - pickerSize) >= 3
    ) {
      this.setState({ pickerSize });
    }

    // We need to get pageX/pageY, ie. the absolute position of the picker on
    // the screen. This is because PanResponder's relative position information
    // is double broken (#12591, #15290). Unfortunately, the only way to get
    // absolute positioning for a View is via measure() after onLayout (#10556).
    InteractionManager.runAfterInteractions(() => {
      if (!this._pickerContainer) {
        return;
      }
      this._pickerContainer.measure((x, y, width, height, pageX, pageY) => {
        this._pageX = pageX;
        this._pageY = pageY;
      });
    });
  }

  _computeHValue(x: number, y: number) {
    const pickerSize = this.state.pickerSize;
    invariant(
      pickerSize !== null && pickerSize !== undefined,
      "pickerSize should be set",
    );
    const dx = x - pickerSize / 2;
    const dy = y - pickerSize / 2;
    const rad = Math.atan2(dx, dy) + Math.PI + Math.PI / 2;
    return rad * 180 / Math.PI % 360;
  }

  _hValueToRad(deg: number) {
    const rad = deg * Math.PI / 180;
{'fraction_non_alphanumeric': 0.13748601910006022, 'fraction_numerical': 0.007399122429665319, 'mean_word_length': 2.955086764205512, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 51, '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': '4905852', 'n_tokens_mistral': 3857, 'n_tokens_neox': 3746, 'n_words': 885}