mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 10:21:35 +03:00
refactor: image preview plugin (#3457)
This commit is contained in:
parent
be3909370e
commit
52809a2783
@ -81,6 +81,9 @@ export const createGlobalThis = () => {
|
||||
TextDecoder: globalThis.TextDecoder,
|
||||
Request: globalThis.Request,
|
||||
Error: globalThis.Error,
|
||||
// bookmark uses these
|
||||
Blob: globalThis.Blob,
|
||||
ClipboardItem: globalThis.ClipboardItem,
|
||||
|
||||
// fixme: use our own db api
|
||||
indexedDB: globalThis.indexedDB,
|
||||
|
@ -37,6 +37,7 @@ const builtinPluginUrl = new Set([
|
||||
'/plugins/bookmark',
|
||||
'/plugins/copilot',
|
||||
'/plugins/hello-world',
|
||||
'/plugins/image-preview',
|
||||
]);
|
||||
|
||||
const logger = new DebugLogger('register-plugins');
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { BlockHubWrapper } from '@affine/component/block-hub';
|
||||
import { BlockSuiteEditor } from '@affine/component/block-suite-editor';
|
||||
import { ImagePreviewModal } from '@affine/component/image-preview-modal';
|
||||
import { initEmptyPage } from '@affine/env/blocksuite';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||
import { ImagePreviewModal } from '@affine/image-preview-plugin/src/component';
|
||||
import { rootBlockHubAtom } from '@affine/workspace/atom';
|
||||
import { getOrCreateWorkspace } from '@affine/workspace/manager';
|
||||
import type { Meta } from '@storybook/react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
export default {
|
||||
title: 'Component/ImagePreviewModal',
|
||||
@ -52,6 +54,10 @@ export const Default = () => {
|
||||
}}
|
||||
>
|
||||
<BlockSuiteEditor mode="page" page={page} onInit={initEmptyPage} />
|
||||
{createPortal(
|
||||
<ImagePreviewModal pageId={page.id} workspace={page.workspace} />,
|
||||
document.body
|
||||
)}
|
||||
</div>
|
||||
<BlockHubWrapper
|
||||
style={{
|
||||
|
@ -18,6 +18,9 @@
|
||||
{
|
||||
"path": "../../packages/workspace"
|
||||
},
|
||||
{
|
||||
"path": "../../plugins/image-preview"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
|
@ -45,7 +45,8 @@ const external = [
|
||||
/^@toeverything/,
|
||||
|
||||
// react
|
||||
/^react/,
|
||||
'react',
|
||||
/^react\//,
|
||||
/^react-dom/,
|
||||
|
||||
// store
|
||||
@ -72,11 +73,11 @@ const json: z.infer<typeof packageJsonInputSchema> = await readFile(
|
||||
)
|
||||
.then(text => JSON.parse(text))
|
||||
.then(async json => {
|
||||
const { success } = await packageJsonInputSchema.safeParseAsync(json);
|
||||
if (success) {
|
||||
const result = await packageJsonInputSchema.safeParseAsync(json);
|
||||
if (result.success) {
|
||||
return json;
|
||||
} else {
|
||||
throw new Error('invalid package.json');
|
||||
throw new Error('invalid package.json', result.error);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -8,8 +8,7 @@ import { Skeleton } from '@mui/material';
|
||||
import { use } from 'foxact/use';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import type { CSSProperties, ReactElement } from 'react';
|
||||
import { lazy, memo, Suspense, useCallback, useEffect, useRef } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { memo, Suspense, useCallback, useEffect, useRef } from 'react';
|
||||
import type { FallbackProps } from 'react-error-boundary';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
|
||||
@ -41,12 +40,6 @@ declare global {
|
||||
var currentEditor: EditorContainer | undefined;
|
||||
}
|
||||
|
||||
const ImagePreviewModal = lazy(() =>
|
||||
import('../image-preview-modal').then(module => ({
|
||||
default: module.ImagePreviewModal,
|
||||
}))
|
||||
);
|
||||
|
||||
const BlockSuiteEditorImpl = (props: EditorProps): ReactElement => {
|
||||
const { onLoad, page, mode, style } = props;
|
||||
if (!page.loaded) {
|
||||
@ -191,17 +184,6 @@ export const BlockSuiteEditor = memo(function BlockSuiteEditor(
|
||||
<Suspense fallback={<BlockSuiteFallback />}>
|
||||
<BlockSuiteEditorImpl {...props} />
|
||||
</Suspense>
|
||||
{props.page && (
|
||||
<Suspense fallback={null}>
|
||||
{createPortal(
|
||||
<ImagePreviewModal
|
||||
workspace={props.page.workspace}
|
||||
pageId={props.page.id}
|
||||
/>,
|
||||
document.body
|
||||
)}
|
||||
</Suspense>
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
);
|
||||
});
|
||||
|
20
plugins/image-preview/package.json
Normal file
20
plugins/image-preview/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@affine/image-preview-plugin",
|
||||
"version": "0.8.0-canary.0",
|
||||
"description": "Image preview plugin",
|
||||
"affinePlugin": {
|
||||
"release": true,
|
||||
"entry": {
|
||||
"core": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@affine/component": "workspace:*",
|
||||
"@blocksuite/icons": "^2.1.27",
|
||||
"@toeverything/plugin-infra": "workspace:*",
|
||||
"@toeverything/theme": "^0.7.9",
|
||||
"clsx": "^2.0.0",
|
||||
"react-error-boundary": "^4.0.10",
|
||||
"swr": "2.1.5"
|
||||
}
|
||||
}
|
11
plugins/image-preview/src/app.tsx
Normal file
11
plugins/image-preview/src/app.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import type { Page } from '@blocksuite/store';
|
||||
|
||||
import { ImagePreviewModal } from './component';
|
||||
|
||||
export type AppProps = {
|
||||
page: Page;
|
||||
};
|
||||
|
||||
export const App = ({ page }: AppProps) => {
|
||||
return <ImagePreviewModal pageId={page.id} workspace={page.workspace} />;
|
||||
};
|
@ -5,8 +5,8 @@ export const hasAnimationPlayedAtom = atom<boolean | null>(true);
|
||||
|
||||
previewBlockIdAtom.onMount = set => {
|
||||
const callback = (event: MouseEvent) => {
|
||||
const target = event.target;
|
||||
if (target instanceof HTMLImageElement) {
|
||||
const target = event.target as HTMLElement | null;
|
||||
if (target?.tagName === 'IMG') {
|
||||
const imageBlock = target.closest('affine-image');
|
||||
if (imageBlock) {
|
||||
const blockId = imageBlock.getAttribute('data-block-id');
|
@ -1,5 +1,4 @@
|
||||
import '@blocksuite/blocks';
|
||||
|
||||
import { Button, IconButton, Tooltip } from '@affine/component';
|
||||
import type { ImageBlockModel } from '@blocksuite/blocks';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import {
|
||||
@ -23,8 +22,6 @@ import type { FallbackProps } from 'react-error-boundary';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { Button, IconButton } from '../../ui/button';
|
||||
import { Tooltip } from '../../ui/tooltip';
|
||||
import { useZoomControls } from './hooks/use-zoom';
|
||||
import {
|
||||
buttonStyle,
|
18
plugins/image-preview/src/index.ts
Normal file
18
plugins/image-preview/src/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import type { PluginContext } from '@toeverything/plugin-infra/entry';
|
||||
import { createElement } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import { App } from './app';
|
||||
|
||||
export const entry = (context: PluginContext) => {
|
||||
context.register('editor', (div, editor) => {
|
||||
const root = createRoot(div);
|
||||
root.render(createElement(App, { page: editor.page }));
|
||||
return () => {
|
||||
root.unmount();
|
||||
};
|
||||
});
|
||||
return () => {
|
||||
// do nothing
|
||||
};
|
||||
};
|
17
plugins/image-preview/tsconfig.json
Normal file
17
plugins/image-preview/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["./src"],
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"outDir": "lib",
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../../packages/plugin-infra"
|
||||
},
|
||||
{
|
||||
"path": "../../packages/component"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
spawnSync('yarn', ['-T', 'run', 'dev-plugin', '--plugin', 'bookmark'], {
|
||||
const builtInPlugins = ['bookmark', 'hello-world', 'copilot', 'image-preview'];
|
||||
|
||||
for (const plugin of builtInPlugins) {
|
||||
const cp = spawn('yarn', ['-T', 'run', 'dev-plugin', '--plugin', plugin], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
|
||||
spawnSync('yarn', ['-T', 'run', 'dev-plugin', '--plugin', 'hello-world'], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
|
||||
spawnSync('yarn', ['-T', 'run', 'dev-plugin', '--plugin', 'copilot'], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
});
|
||||
cp.on('exit', code => {
|
||||
if (code !== 0) {
|
||||
process.exit(code);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ test('plugin should exist', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitEditorLoad(page);
|
||||
await page.route('**/plugins/**/package.json', route => route.fetch(), {
|
||||
times: 3,
|
||||
times: 4,
|
||||
});
|
||||
await page.waitForTimeout(50);
|
||||
const packageJson = await page.evaluate(() => {
|
||||
@ -17,14 +17,26 @@ test('plugin should exist', async ({ page }) => {
|
||||
expect(packageJson).toEqual([
|
||||
{
|
||||
name: '@affine/bookmark-plugin',
|
||||
version: expect.any(String),
|
||||
description: expect.any(String),
|
||||
affinePlugin: expect.anything(),
|
||||
},
|
||||
{
|
||||
name: '@affine/copilot-plugin',
|
||||
version: expect.any(String),
|
||||
description: expect.any(String),
|
||||
affinePlugin: expect.anything(),
|
||||
},
|
||||
{
|
||||
name: '@affine/hello-world-plugin',
|
||||
version: expect.any(String),
|
||||
description: expect.any(String),
|
||||
affinePlugin: expect.anything(),
|
||||
},
|
||||
{
|
||||
name: '@affine/image-preview-plugin',
|
||||
version: expect.any(String),
|
||||
description: expect.any(String),
|
||||
affinePlugin: expect.anything(),
|
||||
},
|
||||
]);
|
||||
|
14
yarn.lock
14
yarn.lock
@ -403,6 +403,20 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@affine/image-preview-plugin@workspace:plugins/image-preview":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@affine/image-preview-plugin@workspace:plugins/image-preview"
|
||||
dependencies:
|
||||
"@affine/component": "workspace:*"
|
||||
"@blocksuite/icons": ^2.1.27
|
||||
"@toeverything/plugin-infra": "workspace:*"
|
||||
"@toeverything/theme": ^0.7.9
|
||||
clsx: ^2.0.0
|
||||
react-error-boundary: ^4.0.10
|
||||
swr: 2.1.5
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@affine/jotai@workspace:*, @affine/jotai@workspace:packages/jotai":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@affine/jotai@workspace:packages/jotai"
|
||||
|
Loading…
Reference in New Issue
Block a user