Загрузка данных


import { Dispatch, MutableRefObject, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';

import { communicator } from '@core/comm';
import { useAppSelect } from '@hooks/useAppSelector';
import { Contract } from '@modules/contracts';
import { CORPACTIONS_OPEN_EXESTED_WIDGET_EVENT, HIGHLIGHT_WIDGET_EVENT } from '@modules/widgets/shared';
import { addContentPropsToWidget, unbindWidgets } from '@store/slices/widgets';

import { getState } from '@store/store';
import { useWidgetsBind } from '@utils/hooks/useWidgetsBind';

import { DEFAULT_SYMBOL } from '../const';

import { useChartPublicContext } from './useChartPublicContext';

import type { Widget } from 'types/Widgets';
import type { WidgetProperties } from '../properties/types';
import type { ChartContainerProps } from '../types';

interface UseChartComponentFacadeReturn {
  dropDownOpen: boolean;
  setDropdownOpen: Dispatch<SetStateAction<boolean>>;
  currentInstrument: string;
  isWidgetHeaderContextMenuOpen: boolean;
  setIsWidgetHeaderContextMenuOpen: Dispatch<SetStateAction<boolean>>;
  onDropInstruments: (val: string, withUpdate?: boolean) => void;
  addInstrumentFromModal: (instruments: Contract[]) => void;
  isOver: boolean;
}

export default function useChartComponentFacade(props: ChartContainerProps): UseChartComponentFacadeReturn {
  const { widgetId } = props;

  const [isOver, setIsOver] = useState<boolean>(false);

  const chartContainerRef = useRef<HTMLDivElement | null>(null) as MutableRefObject<HTMLDivElement>;
  const dispatch = useDispatch();
  const indicativeData = props.widgetContentProps?.indicativeData;

  const chartPrevStateRef = useRef<object>({});

  // используется для определения источника отображаемого в виджете инструмента (из привязки или нет)
  const isInstrumentChangedFromBinding = useRef<boolean>(false);
  const prevTimeframe = useRef<string>('');

  const lastIntervalChangeTime = useRef<number | null>(null);
  const timeDiffOfIntervalChangeTime = useRef<number | null>(null);

  const [isWidgetHeaderContextMenuOpen, setIsWidgetHeaderContextMenuOpen] = useState(false);

  const setChartIsReady = useCallback((isReady: boolean) => {
    setChartIsReadyFromUseState(isReady);
    isChartIsReadyFromRef.current = isReady;
  }, []);

  const language = useAppSelect((state) => state.localisation.localisation);

  const { chartState } = useAppSelect((state) => state.widgets.widgets.find(({ id }) => id === widgetId))
    ?.widgetContentProps as WidgetProperties;

  const [currentInstrument, setCurrentInstrument] = useState(chartState.savedInstrument ?? DEFAULT_SYMBOL);

  const { triggerRelatedWidgetsToUpdate, getMasterInstrumentFromPublicContext } = useWidgetsBind({
    widgetId,
  });

  // Функция работает внутри интервала, поэтому используем не хуковый доступ к стору
  const saveContentProps = (
    val: Partial<WidgetProperties['chartState']> & { withUpdate?: boolean; cleanIndicativeData?: boolean },
  ): void => {
    // если вызываем функцию с withUpdate, то оправляем в стор и делаем запрос на изменения
    const { withUpdate, cleanIndicativeData, ...chartStateVal } = val;
    const oldProps = (getState().widgets.widgets.find(({ id }) => id === widgetId) as Widget)?.widgetContentProps;

    if (oldProps) {
      dispatch(
        addContentPropsToWidget({
          id: widgetId,
          withoutSend: !withUpdate,
          widgetContentProps: {
            chartState: { ...oldProps.chartState, ...chartStateVal },
            // при смене инструмента очищаем индикативные данные, переданные из виджета Индикативные котировки
            indicativeData: cleanIndicativeData ? undefined : oldProps.indicativeData,
          },
        }),
      );
    }
  };

  useEffect(() => {
    triggerRelatedWidgetsToUpdate(currentInstrument.current);

    const savedActiveChart = chartIsReady ? savedTvWidget?.activeChart?.() : false;

    if (!chartIsReady || !tvWidgetRef.current || !savedTvWidget || !savedActiveChart) {
      return;
    }

    if (currentInstrument.current !== savedActiveChart.symbol()) {
      savedActiveChart.setSymbol(currentInstrument.current);
    }
    // [currentInstrument.current] в зависимостях
    // т.к. здесь не нужен ререндер потому что достаточно прокинуть в TV новое значение
    // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO Разобраться с зависимостями
  }, [currentInstrument.current, chartIsReady]);

  const onInstrumentChange = useCallback(
    (newVal: string, withUpdate?: boolean, unbind = true): void => {
      const prev = currentInstrument.current;
      currentInstrument.current = newVal;

      if (unbind) {
        dispatch(unbindWidgets({ widgetId }));
      }

      // при смене инструмента очищаем индикативные данные виджета график
      // т.к. логика для графика индикатива построена на наличии в widgetContentProps данных indicativeData
      saveContentProps({ savedInstrument: newVal, withUpdate, cleanIndicativeData: true });
      setIsWidgetHeaderContextMenuOpen(false);
      onInstrumentChangeCallback(prev, newVal);
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO Разобраться с зависимостями
    [onInstrumentChangeCallback, chartIsReady],
  );

  const addInstrumentFromModal = useCallback(
    (instruments: Pick<Contract, 'issKey'>[]) => {
      const instr = instruments[0];

      isInstrumentChangedFromBinding.current = false;

      if (instr.issKey !== null) {
        onInstrumentChange(instr.issKey);
      }
    },
    [onInstrumentChange],
  );

  const onInstrumentChangeFromBind = useCallback(
    (instrumentId: string) => {
      isInstrumentChangedFromBinding.current = true;
      onInstrumentChange(instrumentId, true, false);
    },
    [onInstrumentChange],
  );

  const onDropInstruments = useCallback((val: string, withUpdate?: boolean): void => {
    isInstrumentChangedFromBinding.current = false;
    onInstrumentChange(val, withUpdate);
    // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO Разобраться с зависимостями
  }, []);

  useChartPublicContext({
    setCurrInstrument: onInstrumentChangeFromBind,
    widgetId,
    getMasterInstrumentFromPublicContext,
  });

  const [dropDownOpen, setDropdownOpen] = useState(false);

  useEffect(() => {
    const unsubscribe = communicator.listen({ messageType: CORPACTIONS_OPEN_EXESTED_WIDGET_EVENT }, (message) => {
      const issKey = (message as Record<number, string>)[widgetId];
      if (issKey) {
        addInstrumentFromModal([{ issKey }]);
      }
    });
    const unsubscribeHighlighter = communicator.listen({ messageType: HIGHLIGHT_WIDGET_EVENT }, (message) => {
      const typedMessage = message as Record<number, boolean>;

      if (Object.keys(typedMessage).includes(String(widgetId))) {
        setIsOver(typedMessage[widgetId]);
      }
    });
    return () => {
      unsubscribe();
      unsubscribeHighlighter();
    };
  }, [addInstrumentFromModal, widgetId]);

  /** Переключение инструмента и вида графика при indicativeData */
  useEffect(() => {
    const savedActiveChart = chartIsReady ? savedTvWidget?.activeChart?.() : false;

    if (!chartIsReady || !tvWidgetRef.current || !savedTvWidget || !savedActiveChart) {
      return;
    }

    onInstrumentChangeCallback(
      chartState?.savedInstrument ?? (DEFAULT_OPTIONS.symbol as string),
      currentInstrument.current,
    );

    if (indicativeData) {
      savedActiveChart.setSymbol(indicativeData.key);
      savedActiveChart.setChartType(SeriesStyle.Line);
      savedActiveChart.setResolution('60' as ResolutionString);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps -- TODO Разобраться с зависимостями
  }, [chartIsReady, savedTvWidget, indicativeData]);

  return {
    chartContainerRef,
    savedTvWidget,
    dropDownOpen,
    setDropdownOpen,
    currentInstrument,
    handlerSaveAsExcel,
    isWidgetHeaderContextMenuOpen,
    setIsWidgetHeaderContextMenuOpen,
    onDropInstruments,
    addInstrumentFromModal,
    isOver,
  };
}



[{
	"resource": "/c:/Users/ShkinderDV/moex-terminal-front/src/widgets/Chart/hooks/useChartComponentFacade.tsx",
	"owner": "typescript",
	"code": "2304",
	"severity": 8,
	"message": "Cannot find name 'setChartIsReadyFromUseState'.",
	"source": "ts",
	"startLineNumber": 53,
	"startColumn": 5,
	"endLineNumber": 53,
	"endColumn": 32,
	"modelVersionId": 187,
	"origin": "extHost1"
}]