fix(component): image preview fallback (#3058)

This commit is contained in:
Alex Yang
2023-07-06 17:22:23 +08:00
committed by GitHub
parent f468dff6aa
commit ce63364299

View File

@@ -16,10 +16,13 @@ import {
} from '@blocksuite/icons'; } from '@blocksuite/icons';
import type { Workspace } from '@blocksuite/store'; import type { Workspace } from '@blocksuite/store';
import clsx from 'clsx'; import clsx from 'clsx';
import { useErrorBoundary } from 'foxact/use-error-boundary';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import type { ReactElement } from 'react'; import type { PropsWithChildren, ReactElement } from 'react';
import { Suspense, useCallback } from 'react'; import { Suspense, useCallback } from 'react';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import type { FallbackProps } from 'react-error-boundary';
import { ErrorBoundary } from 'react-error-boundary';
import useSWR from 'swr'; import useSWR from 'swr';
import { useZoomControls } from './hooks/use-zoom'; import { useZoomControls } from './hooks/use-zoom';
@@ -252,21 +255,28 @@ const ImagePreviewModalImpl = (
assertExists(block); assertExists(block);
setCaption(block?.caption); setCaption(block?.caption);
}, [props.blockId, props.pageId, props.workspace]); }, [props.blockId, props.pageId, props.workspace]);
const { data } = useSWR(['workspace', 'image', props.pageId, props.blockId], { const { data, error } = useSWR(
fetcher: ([_, __, pageId, blockId]) => { ['workspace', 'image', props.pageId, props.blockId],
const page = props.workspace.getPage(pageId); {
assertExists(page); fetcher: ([_, __, pageId, blockId]) => {
const block = page.getBlockById(blockId) as ImageBlockModel; const page = props.workspace.getPage(pageId);
assertExists(block); assertExists(page);
return props.workspace.blobs.get(block?.sourceId); const block = page.getBlockById(blockId) as ImageBlockModel;
}, assertExists(block);
suspense: true, return props.workspace.blobs.get(block?.sourceId);
}); },
suspense: true,
}
);
useErrorBoundary(error);
const [prevData, setPrevData] = useState<string | null>(() => data); const [prevData, setPrevData] = useState<string | null>(() => data);
const [url, setUrl] = useState<string | null>(null); const [url, setUrl] = useState<string | null>(null);
if (prevData !== data) { if (data === null) {
return null;
} else if (prevData !== data) {
if (url) { if (url) {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
@@ -461,6 +471,21 @@ const ImagePreviewModalImpl = (
); );
}; };
const ErrorLogger = (props: FallbackProps) => {
useEffect(() => {
console.error('image preview modal error', props.error);
}, [props.error]);
return null;
};
export const ImagePreviewErrorBoundary = (
props: PropsWithChildren
): ReactElement => {
return (
<ErrorBoundary fallbackRender={ErrorLogger}>{props.children}</ErrorBoundary>
);
};
export const ImagePreviewModal = ( export const ImagePreviewModal = (
props: ImagePreviewModalProps props: ImagePreviewModalProps
): ReactElement | null => { ): ReactElement | null => {
@@ -530,39 +555,43 @@ export const ImagePreviewModal = (
} }
return ( return (
<div <ImagePreviewErrorBoundary>
data-testid="image-preview-modal" <div
className={`${imagePreviewBackgroundStyle} ${isOpen ? loaded : unloaded}`} data-testid="image-preview-modal"
> className={`${imagePreviewBackgroundStyle} ${
<Suspense fallback={<div />}> isOpen ? loaded : unloaded
<ImagePreviewModalImpl }`}
{...props}
blockId={blockId}
onClose={() => setBlockId(null)}
/>
</Suspense>
<button
data-testid="image-preview-close-button"
onClick={() => {
setBlockId(null);
}}
className={imagePreviewModalCloseButtonStyle}
> >
<svg <Suspense>
width="10" <ImagePreviewModalImpl
height="10" {...props}
viewBox="0 0 10 10" blockId={blockId}
fill="none" onClose={() => setBlockId(null)}
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0.286086 0.285964C0.530163 0.0418858 0.925891 0.0418858 1.16997 0.285964L5.00013 4.11613L8.83029 0.285964C9.07437 0.0418858 9.4701 0.0418858 9.71418 0.285964C9.95825 0.530041 9.95825 0.925769 9.71418 1.16985L5.88401 5.00001L9.71418 8.83017C9.95825 9.07425 9.95825 9.46998 9.71418 9.71405C9.4701 9.95813 9.07437 9.95813 8.83029 9.71405L5.00013 5.88389L1.16997 9.71405C0.925891 9.95813 0.530163 9.95813 0.286086 9.71405C0.0420079 9.46998 0.0420079 9.07425 0.286086 8.83017L4.11625 5.00001L0.286086 1.16985C0.0420079 0.925769 0.0420079 0.530041 0.286086 0.285964Z"
fill="#77757D"
/> />
</svg> </Suspense>
</button> <button
</div> data-testid="image-preview-close-button"
onClick={() => {
setBlockId(null);
}}
className={imagePreviewModalCloseButtonStyle}
>
<svg
width="10"
height="10"
viewBox="0 0 10 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0.286086 0.285964C0.530163 0.0418858 0.925891 0.0418858 1.16997 0.285964L5.00013 4.11613L8.83029 0.285964C9.07437 0.0418858 9.4701 0.0418858 9.71418 0.285964C9.95825 0.530041 9.95825 0.925769 9.71418 1.16985L5.88401 5.00001L9.71418 8.83017C9.95825 9.07425 9.95825 9.46998 9.71418 9.71405C9.4701 9.95813 9.07437 9.95813 8.83029 9.71405L5.00013 5.88389L1.16997 9.71405C0.925891 9.95813 0.530163 9.95813 0.286086 9.71405C0.0420079 9.46998 0.0420079 9.07425 0.286086 8.83017L4.11625 5.00001L0.286086 1.16985C0.0420079 0.925769 0.0420079 0.530041 0.286086 0.285964Z"
fill="#77757D"
/>
</svg>
</button>
</div>
</ImagePreviewErrorBoundary>
); );
}; };