export default function CustomFieldsPanel()

in src/components/custom-fields-panel/custom-fields-panel.tsx [95:301]


export default function CustomFieldsPanel(props: Props) {
  const api: Api = getApi();
  let currentScrollX: number = 0;
  const isComponentMounted = React.useRef<boolean>(false);
  const isConnected = useSelector((state: AppState) => state.app.networkState?.isConnected);
  const user = useSelector((state: AppState) => state.app.user);
  const isReporter = !!user?.profiles?.helpdesk?.isReporter;

  const [selectState, setSelectState] = React.useState<SelectState | null>(null);
  const [simpleValueState, setSimpleValue] = React.useState<SimpleValueState | null>(null);
  const [datePickerState, setDatePickerState] = React.useState<DatePickerState>(dataPickerDefault);
  const [isEditingProject, setEditingProject] = React.useState<boolean>(false);
  const [isSavingProject, setSavingProject] = React.useState<boolean>(false);
  const [editingField, setEditingField] = React.useState<IssueCustomField | null>(null);
  const [savingField, setSavingField] = React.useState<IssueCustomField | null>(null);

  React.useEffect(() => {
    isComponentMounted.current = true;
    return () => {
      isComponentMounted.current = false;
    };
  }, []);

  React.useEffect(() => {
    isComponentMounted.current = true;
    return () => {
      isComponentMounted.current = false;
    };
  }, []);

  const trackEvent: (message: string) => void = (message: string) => {
    if (props.analyticsId) {
      usage.trackEvent(props.analyticsId, message);
    }
  };

  const closeEditor = () => {
    setEditingField(null);
    setEditingProject(false);
    setSelectState(null);
    setSimpleValue(null);
    setDatePickerState(dataPickerDefault);
  };

  const saveUpdatedField = async (field: IssueCustomField, value: CustomFieldBaseValue) => {
    const updateSavingState = (f: IssueCustomField | null) => {
      if (isComponentMounted) {
        setSavingField(f);
      }
    };

    closeEditor();
    updateSavingState(field);
    await props.onUpdate(field, value);
    updateSavingState(null);
  };

  const onSelectProject = () => {
    trackEvent('Update project: start');

    if (isEditingProject) {
      return closeEditor();
    }

    const {hasPermission, helpDeskProjectsOnly} = props;
    closeEditor();
    setEditingProject(true);
    setSelectState({
      show: true,
      getValue: (it) => {
        const p = it as Project;
        return `${p.name} (${p.shortName})`;
      },
      dataSource: async query => {
        const projects = await api.getProjects(query);
        return projects
          .filter(project =>
            helpDeskProjectsOnly
              ? project?.plugins?.helpDeskSettings?.enabled
              : !project.plugins?.helpDeskSettings?.enabled
          )
          .filter(project => !project.archived && !project.template)
          .filter(project => hasPermission?.canCreateIssueToProject?.(project));
      },
      multi: false,
      placeholder: i18n('Search for the project'),
      selectedItems: [props.issueProject],
      onSelect: (project: Project) => {
        trackEvent('Update project: updated');
        closeEditor();
        setSavingProject(true);
        return props.onUpdateProject(project).then(() => setSavingProject(false));
      },
    });
  };

  const editDateField = (field: IssueCustomField) => {
    trackEvent('Edit date field');
    const projectCustomField = field.projectCustomField;
    const withTime = projectCustomField.field.fieldType.valueType === DATE_AND_TIME_FIELD_VALUE_TYPE;
    const date = field.value ? new Date(field.value as number) : null;
    return setDatePickerState({
      show: true,
      placeholder: i18n('Enter time value'),
      withTime,
      title: projectCustomField.field.name,
      date,
      emptyValueName: projectCustomField.canBeEmpty ? projectCustomField.emptyFieldText : null,
      onSelect: (d: Date | null) => {
        if (!d) {
          saveUpdatedField(field, null);
        } else {
          saveUpdatedField(field, d.getTime());
        }
      },
    });
  };

  const editSimpleValueField = (field: IssueCustomField, type: keyof typeof customFieldPlaceholders) => {
    trackEvent('Edit simple value field');
    const placeholder: string = customFieldPlaceholders[type] || customFieldPlaceholders.default;
    const valueFormatter = customFieldValueFormatters[type] || customFieldValueFormatters.default;
    const value: string =
      field.value != null
        ? (field.value as PeriodFieldValue)?.presentation || (field.value as TextFieldValue)?.text || `${field.value}`
        : '';

    setSimpleValue({
      show: true,
      placeholder,
      value,
      onApply: (v: string) => {
        saveUpdatedField(field, valueFormatter(v) as CustomFieldValue);
      },
    });
  };

  const editCustomField = (field: IssueCustomField) => {
    const projectCustomField = field.projectCustomField;
    const projectCustomFieldName: string | null | undefined = projectCustomField?.field?.name;
    trackEvent(`Edit custom field: ${projectCustomFieldName ? projectCustomFieldName.toLowerCase() : ''}`);

    const p = getCustomFieldSelectProps({
      field,
      issueId: props.issueId,
      onChangeSelection: selectedItems => {
        setSelectState(prevState => ({...prevState, ...selectState, selectedItems}));
      },
      onSelect: value => {
        saveUpdatedField(field, value);
      },
    });

    setSelectState(p);
  };

  const onEditField = (field: IssueCustomField) => {
    if (field === editingField) {
      return closeEditor();
    }

    const {fieldType} = field.projectCustomField?.field;

    if (!fieldType) {
      return null;
    }

    setEditingField(field);
    setEditingProject(false);
    if (fieldType.valueType === 'date' || fieldType.valueType === DATE_AND_TIME_FIELD_VALUE_TYPE) {
      return editDateField(field);
    }

    if (['period', 'integer', 'string', 'text', 'float'].indexOf(fieldType.valueType) !== -1) {
      return editSimpleValueField(field, fieldType.valueType as keyof typeof customFieldPlaceholders);
    }

    return editCustomField(field);
  };

  const storeScrollPosition = (event: Record<string, any>) => {
    const {nativeEvent} = event;
    currentScrollX = nativeEvent.contentOffset.x;
  };

  const restoreScrollPosition = (scrollNode?: ScrollView | null, ensure: boolean = true) => {
    if (!scrollNode || !currentScrollX) {
      return;
    }

    scrollNode.scrollTo({
      x: currentScrollX,
      y: 0,
      animated: false,
    });

    // Android doesn't get first scrollTo call https://youtrack.jetbrains.com/issue/YTM-402
    // iOS doesn't scroll immediately since 0.48 https://github.com/facebook/react-native/issues/15808
    if (ensure) {
      setTimeout(() => restoreScrollPosition(scrollNode, false));
    }
  };

  const renderSelect = () => {
    const Component : React.ElementType = isSplitView() ? SelectModal : Select;
    return <Component {...selectState} autoFocus={props.autoFocusSelect} onCancel={() => closeEditor()} />;
  };