import {BlobResponse, blobFetcher} from '~/utils/fetch'; import React, {useImperativeHandle, useLayoutEffect, useState} from 'react'; import {WithStyled, primaryColor} from '~/utils/style'; import GridLoader from 'react-spinners/GridLoader'; import mime from 'mime-types'; import {saveAs} from 'file-saver'; import useRequest from '~/hooks/useRequest'; import {useTranslation} from 'react-i18next'; export type ImageRef = { save(filename: string): void; }; type ImageProps = { src?: string; cache?: number; }; const Image = React.forwardRef(({src, cache, className}, ref) => { const {t} = useTranslation('common'); const [url, setUrl] = useState(''); const {data, error, loading} = useRequest(src ?? null, blobFetcher, { dedupingInterval: cache ?? 2000 }); useImperativeHandle(ref, () => ({ save: (filename: string) => { if (data) { const ext = data.type ? mime.extension(data.type) : null; saveAs(data.data, filename.replace(/[/\\?%*:|"<>]/g, '_') + (ext ? `.${ext}` : '')); } } })); // use useLayoutEffect hook to prevent image render after url revoked useLayoutEffect(() => { if (data) { let objectUrl: string | null = null; objectUrl = URL.createObjectURL(data.data); setUrl(objectUrl); return () => { objectUrl && URL.revokeObjectURL(objectUrl); }; } }, [data]); if (loading) { return ; } if (error) { return
{t('common:error')}
; } return ; }); export default Image;