export default function DownloadScreenshot()

in superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadScreenshot.tsx [42:203]


export default function DownloadScreenshot({
  text,
  logEvent,
  dashboardId,
  format,
  ...rest
}: {
  text: string;
  dashboardId: number;
  logEvent?: Function;
  format: string;
}) {
  const activeTabs = useSelector(
    (state: RootState) => state.dashboardState.activeTabs || undefined,
  );
  const anchor = useSelector(
    (state: RootState) =>
      last(state.dashboardState.directPathToChild) || undefined,
  );
  const dataMask = useSelector(
    (state: RootState) => state.dataMask || undefined,
  );
  const { addDangerToast, addSuccessToast, addInfoToast } = useToasts();
  const currentIntervalIds = useRef<NodeJS.Timeout[]>([]);

  const printLoadingToast = () =>
    addInfoToast(
      t('The screenshot is being generated. Please, do not leave the page.'),
      {
        noDuplicate: true,
      },
    );

  const printFailureToast = useCallback(
    () =>
      addDangerToast(
        t('The screenshot could not be downloaded. Please, try again later.'),
      ),
    [addDangerToast],
  );

  const printSuccessToast = useCallback(
    () => addSuccessToast(t('The screenshot has been downloaded.')),
    [addSuccessToast],
  );

  const stopIntervals = useCallback(
    (message?: 'success' | 'failure') => {
      currentIntervalIds.current.forEach(clearInterval);

      if (message === 'failure') {
        printFailureToast();
      }
      if (message === 'success') {
        printSuccessToast();
      }
    },
    [printFailureToast, printSuccessToast],
  );

  const onDownloadScreenshot = () => {
    let retries = 0;

    const toastIntervalId = setInterval(
      () => printLoadingToast(),
      RETRY_INTERVAL,
    );

    currentIntervalIds.current = [
      ...(currentIntervalIds.current || []),
      toastIntervalId,
    ];

    printLoadingToast();

    // this function checks if the image is ready
    const checkImageReady = (cacheKey: string) =>
      SupersetClient.get({
        endpoint: `/api/v1/dashboard/${dashboardId}/screenshot/${cacheKey}/?download_format=${format}`,
        headers: { Accept: 'application/pdf, image/png' },
        parseMethod: 'raw',
      })
        .then((response: Response) => response.blob())
        .then(blob => {
          const url = window.URL.createObjectURL(blob);
          const a = document.createElement('a');
          a.href = url;
          a.download = `screenshot.${format}`;
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          window.URL.revokeObjectURL(url);
          stopIntervals('success');
        })
        .catch(err => {
          if ((err as SupersetApiError).status === 404) {
            throw new Error('Image not ready');
          }
        });

    const fetchImageWithRetry = (cacheKey: string) => {
      if (retries >= MAX_RETRIES) {
        stopIntervals('failure');
        logging.error('Max retries reached');
        return;
      }
      checkImageReady(cacheKey).catch(() => {
        retries += 1;
      });
    };

    SupersetClient.post({
      endpoint: `/api/v1/dashboard/${dashboardId}/cache_dashboard_screenshot/`,
      jsonPayload: {
        anchor,
        activeTabs,
        dataMask,
        urlParams: getDashboardUrlParams(['edit']),
      },
    })
      .then(({ json }) => {
        const cacheKey = json?.cache_key;
        if (!cacheKey) {
          throw new Error('No image URL in response');
        }
        const retryIntervalId = setInterval(() => {
          fetchImageWithRetry(cacheKey);
        }, RETRY_INTERVAL);
        currentIntervalIds.current.push(retryIntervalId);
        fetchImageWithRetry(cacheKey);
      })
      .catch(error => {
        logging.error(error);
        stopIntervals('failure');
      })
      .finally(() => {
        logEvent?.(
          format === DownloadScreenshotFormat.PNG
            ? LOG_ACTIONS_DASHBOARD_DOWNLOAD_AS_IMAGE
            : LOG_ACTIONS_DASHBOARD_DOWNLOAD_AS_PDF,
        );
      });
  };

  useEffect(
    () => () => {
      if (currentIntervalIds.current.length > 0) {
        stopIntervals();
      }
      currentIntervalIds.current = [];
    },
    [stopIntervals],
  );

  return (
    <Menu.Item key={format} {...rest}>
      <div onClick={onDownloadScreenshot} role="button" tabIndex={0}>
        {text}
      </div>
    </Menu.Item>
  );
}