Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
useLoadPlugin.ts 3.70 KiB
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { isActiveLegendSelector } from '@/redux/legend/legend.selectors';
import { removePluginLegend, setDefaultLegendId } from '@/redux/legend/legend.slice';
import {
  allActivePluginsSelector,
  isPluginActiveSelector,
  isPluginLoadingSelector,
  isPluginSelectedSelector,
} from '@/redux/plugins/plugins.selectors';
import { removePlugin, setCurrentDrawerPluginHash } from '@/redux/plugins/plugins.slice';
import { PluginsManager } from '@/services/pluginsManager';
import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
import { getErrorMessage } from '@/utils/getErrorMessage';
import { showToast } from '@/utils/showToast';
import axios from 'axios';
import { PLUGIN_LOADING_ERROR_PREFIX } from '../../AvailablePluginsDrawer.constants';

type UseLoadPluginReturnType = {
  togglePlugin: () => void;
  loadPlugin: () => Promise<void>;
  unloadPlugin: () => void;
  reloadPlugin: () => void;
  isPluginSelected: boolean;
  isPluginActive: boolean;
  isPluginLoading: boolean;
};

type UseLoadPluginProps = {
  hash: string;
  pluginUrl: string;
  onPluginLoaded?(): void;
};

export const useLoadPlugin = ({
  hash,
  pluginUrl,
  onPluginLoaded,
}: UseLoadPluginProps): UseLoadPluginReturnType => {
  const isPluginActive = useAppSelector(state => isPluginActiveSelector(state, hash));
  const isPluginLoading = useAppSelector(state => isPluginLoadingSelector(state, hash));
  const isPluginSelected = useAppSelector(state => isPluginSelectedSelector(state, hash));
  const isActivePluginLegend = useAppSelector(state => isActiveLegendSelector(state, hash));
  const allActivePlugins = useAppSelector(allActivePluginsSelector);

  const dispatch = useAppDispatch();

  const setLastPluginAsCurrentActivePlugin = (): void => {
    const newAllActivePlugins = allActivePlugins.filter(p => p.hash !== hash);
    const lastActivePlugin = newAllActivePlugins.pop();
    if (lastActivePlugin) {
      dispatch(setCurrentDrawerPluginHash(lastActivePlugin.hash));
    }
  };

  const handleLoadPlugin = async (): Promise<void> => {
    try {
      const response = await axios(pluginUrl);
      let pluginScript = response.data;

      PluginsManager.setHashedPlugin({
        pluginUrl,
        pluginScript,
      });

      pluginScript += `//# sourceURL=${pluginUrl}`;

      /* eslint-disable no-new-func */
      const loadPlugin = new Function(pluginScript);
      loadPlugin();

      if (onPluginLoaded) {
        onPluginLoaded();
      }
    } catch (error) {
      const errorMessage = getErrorMessage({
        error,
        prefix: PLUGIN_LOADING_ERROR_PREFIX,
      });
      showToast({ type: 'error', message: errorMessage });
    }
  };

  const handleRemoveLegend = (): void => {
    if (isActivePluginLegend) {
      dispatch(setDefaultLegendId());
    }

    dispatch(removePluginLegend(hash));
  };

  const handleUnloadPlugin = (): void => {
    dispatch(removePlugin({ pluginId: hash }));

    setLastPluginAsCurrentActivePlugin();

    handleRemoveLegend();

    PluginsManager.removePluginContent({ hash });

    PluginsManager.unloadActivePlugin(hash);

    PluginsEventBus.dispatchEvent('onPluginUnload', { hash });
  };

  const handleReloadPlugin = async (): Promise<void> => {
    handleUnloadPlugin();
    await handleLoadPlugin();
  };

  const togglePlugin = async (): Promise<void> => {
    if (isPluginActive) {
      handleUnloadPlugin();
    } else {
      await handleLoadPlugin();
    }
  };

  return {
    isPluginSelected,
    togglePlugin,
    loadPlugin: handleLoadPlugin,
    unloadPlugin: handleUnloadPlugin,
    reloadPlugin: handleReloadPlugin,
    isPluginActive,
    isPluginLoading,
  };
};