Image.tsx 1.7 KB
Newer Older
1 2
import {BlobResponse, blobFetcher} from '~/utils/fetch';
import React, {useImperativeHandle, useLayoutEffect, useState} from 'react';
P
Peter Pan 已提交
3
import {WithStyled, primaryColor} from '~/utils/style';
4 5

import GridLoader from 'react-spinners/GridLoader';
6 7
import mime from 'mime-types';
import {saveAs} from 'file-saver';
8
import useRequest from '~/hooks/useRequest';
9
import {useTranslation} from 'react-i18next';
10

11 12 13 14
export type ImageRef = {
    save(filename: string): void;
};

15 16
type ImageProps = {
    src?: string;
17
    cache?: number;
18 19
};

P
Peter Pan 已提交
20
const Image = React.forwardRef<ImageRef, ImageProps & WithStyled>(({src, cache, className}, ref) => {
21 22
    const {t} = useTranslation('common');

23
    const [url, setUrl] = useState('');
24
    const {data, error, loading} = useRequest<BlobResponse>(src ?? null, blobFetcher, {
25
        dedupingInterval: cache ?? 2000
26
    });
27

28 29 30 31 32 33 34 35 36
    useImperativeHandle(ref, () => ({
        save: (filename: string) => {
            if (data) {
                const ext = data.type ? mime.extension(data.type) : null;
                saveAs(data.data, filename.replace(/[/\\?%*:|"<>]/g, '_') + (ext ? `.${ext}` : ''));
            }
        }
    }));

P
Peter Pan 已提交
37 38
    // use useLayoutEffect hook to prevent image render after url revoked
    useLayoutEffect(() => {
39
        if (data) {
40
            let objectUrl: string | null = null;
41
            objectUrl = URL.createObjectURL(data.data);
P
Peter Pan 已提交
42
            setUrl(objectUrl);
43 44 45 46
            return () => {
                objectUrl && URL.revokeObjectURL(objectUrl);
            };
        }
P
Peter Pan 已提交
47
    }, [data]);
48

49 50 51 52 53
    if (loading) {
        return <GridLoader color={primaryColor} size="10px" />;
    }

    if (error) {
P
Peter Pan 已提交
54
        return <div>{t('common:error')}</div>;
55 56
    }

P
Peter Pan 已提交
57
    return <img className={className} src={url} />;
58
});
59 60

export default Image;