///
import '@blocksuite/blocks';
import type { EmbedBlockModel } from '@blocksuite/blocks';
import { assertExists } from '@blocksuite/global/utils';
import type { Workspace } from '@blocksuite/store';
import { useAtom } from 'jotai';
import type { ReactElement } from 'react';
import { Suspense, useCallback } from 'react';
import { useEffect, useRef, useState } from 'react';
import useSWR from 'swr';
import {
imagePreviewModalCloseButtonStyle,
imagePreviewModalContainerStyle,
imagePreviewModalGoStyle,
imagePreviewModalImageStyle,
imagePreviewModalStyle,
} from './index.css';
import { previewBlockIdAtom } from './index.jotai';
export type ImagePreviewModalProps = {
workspace: Workspace;
pageId: string;
};
const ImagePreviewModalImpl = (
props: ImagePreviewModalProps & {
blockId: string;
onClose: () => void;
}
): ReactElement | null => {
const [blockId, setBlockId] = useAtom(previewBlockIdAtom);
const [caption, setCaption] = useState(() => {
const page = props.workspace.getPage(props.pageId);
assertExists(page);
const block = page.getBlockById(props.blockId) as EmbedBlockModel | null;
assertExists(block);
return block.caption;
});
useEffect(() => {
const page = props.workspace.getPage(props.pageId);
assertExists(page);
const block = page.getBlockById(props.blockId) as EmbedBlockModel | null;
assertExists(block);
const disposable = block.propsUpdated.on(() => {
setCaption(block.caption);
});
return () => {
disposable.dispose();
};
}, [props.blockId, props.pageId, props.workspace]);
const { data } = useSWR(['workspace', 'embed', props.pageId, props.blockId], {
fetcher: ([_, __, pageId, blockId]) => {
const page = props.workspace.getPage(pageId);
assertExists(page);
const block = page.getBlockById(blockId) as EmbedBlockModel | null;
assertExists(block);
return props.workspace.blobs.get(block.sourceId);
},
suspense: true,
});
const [prevData, setPrevData] = useState(() => data);
const [url, setUrl] = useState(null);
const imageRef = useRef(null);
if (prevData !== data) {
if (url) {
URL.revokeObjectURL(url);
}
setUrl(URL.createObjectURL(data));
setPrevData(data);
} else if (!url) {
setUrl(URL.createObjectURL(data));
}
if (!url) {
return null;
}
return (
{
assertExists(blockId);
const workspace = props.workspace;
const page = workspace.getPage(props.pageId);
assertExists(page);
const block = page.getBlockById(blockId);
assertExists(block);
const prevBlock = page
.getPreviousSiblings(block)
.findLast(
(block): block is EmbedBlockModel =>
block.flavour === 'affine:embed'
);
if (prevBlock) {
setBlockId(prevBlock.id);
}
}}
>
❮
{
assertExists(blockId);
const workspace = props.workspace;
const page = workspace.getPage(props.pageId);
assertExists(page);
const block = page.getBlockById(blockId);
assertExists(block);
const nextBlock = page
.getNextSiblings(block)
.find(
(block): block is EmbedBlockModel =>
block.flavour === 'affine:embed'
);
if (nextBlock) {
setBlockId(nextBlock.id);
}
}}
>
❯
);
};
export const ImagePreviewModal = (
props: ImagePreviewModalProps
): ReactElement | null => {
const [blockId, setBlockId] = useAtom(previewBlockIdAtom);
const handleKeyUp = useCallback(
(event: KeyboardEvent) => {
if (event.key === 'Escape') {
event.preventDefault();
event.stopPropagation();
setBlockId(null);
return;
}
if (!blockId) {
return;
}
const workspace = props.workspace;
const page = workspace.getPage(props.pageId);
assertExists(page);
const block = page.getBlockById(blockId);
assertExists(block);
if (event.key === 'ArrowLeft') {
const prevBlock = page
.getPreviousSiblings(block)
.findLast(
(block): block is EmbedBlockModel =>
block.flavour === 'affine:embed'
);
if (prevBlock) {
setBlockId(prevBlock.id);
}
} else if (event.key === 'ArrowRight') {
const nextBlock = page
.getNextSiblings(block)
.find(
(block): block is EmbedBlockModel =>
block.flavour === 'affine:embed'
);
if (nextBlock) {
setBlockId(nextBlock.id);
}
} else {
return;
}
event.preventDefault();
event.stopPropagation();
},
[blockId, setBlockId, props.workspace, props.pageId]
);
useEffect(() => {
document.addEventListener('keyup', handleKeyUp);
return () => {
document.removeEventListener('keyup', handleKeyUp);
};
}, [handleKeyUp]);
if (!blockId) {
return null;
}
return (
}>
setBlockId(null)}
/>
);
};