Skip to content

Commit

Permalink
feat: pdf module
Browse files Browse the repository at this point in the history
  • Loading branch information
fundon committed Nov 8, 2024
1 parent e8eeca3 commit 7f5c1ad
Show file tree
Hide file tree
Showing 17 changed files with 766 additions and 579 deletions.
22 changes: 21 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,26 @@
"which-typed-array": "npm:@nolyfill/which-typed-array@latest",
"@reforged/maker-appimage/@electron-forge/maker-base": "7.5.0",
"macos-alias": "npm:@napi-rs/[email protected]",
"fs-xattr": "npm:@napi-rs/xattr@latest"
"fs-xattr": "npm:@napi-rs/xattr@latest",
"@blocksuite/affine": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/all",
"@blocksuite/affine-block-embed": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/block-embed",
"@blocksuite/affine-block-list": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/block-list",
"@blocksuite/affine-block-paragraph": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/block-paragraph",
"@blocksuite/affine-block-surface": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/block-surface",
"@blocksuite/affine-components": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/components",
"@blocksuite/data-view": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/data-view",
"@blocksuite/affine-model": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/model",
"@blocksuite/affine-shared": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/shared",
"@blocksuite/affine-widget-scroll-anchoring": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/affine/widget-scroll-anchoring",
"@blocksuite/blocks": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/blocks",
"@blocksuite/block-std": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/framework/block-std",
"@blocksuite/global": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/framework/global",
"@blocksuite/inline": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/framework/inline",
"@blocksuite/store": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/framework/store",
"@blocksuite/sync": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/framework/sync",
"@blocksuite/presets": "portal:/Users/fundon/dev/toeverything/blocksuite/packages/presets",
"@toeverything/pdf-viewer": "portal:/Users/fundon/dev/toeverything/pdfium-builder/packages/pdf-viewer",
"@toeverything/pdfium": "portal:/Users/fundon/dev/toeverything/pdfium-builder/packages/pdfium",
"@toeverything/pdf-viewer-types": "portal:/Users/fundon/dev/toeverything/pdfium-builder/packages/types"
}
}
2 changes: 1 addition & 1 deletion packages/frontend/component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-tooltip": "^1.0.7",
"@radix-ui/react-visually-hidden": "^1.1.0",
"@toeverything/pdf-viewer": "^0.1.0",
"@toeverything/pdf-viewer": "^0.1.1",
"@toeverything/theme": "^1.0.17",
"@vanilla-extract/dynamic": "^2.1.0",
"check-password-strength": "^2.0.10",
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-toolbar": "^1.0.4",
"@sentry/react": "^8.0.0",
"@toeverything/pdf-viewer": "^0.1.0",
"@toeverything/pdf-viewer": "^0.1.1",
"@toeverything/theme": "^1.0.17",
"@vanilla-extract/dynamic": "^2.1.0",
"animejs": "^3.2.2",
Expand Down
193 changes: 87 additions & 106 deletions packages/frontend/core/src/components/attachment-viewer/viewer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { IconButton, observeResize, Scrollable } from '@affine/component';
import {
type PDFChannel,
PDFService,
type PDFWorker,
} from '@affine/core/modules/pdf';
import { MessageOp, RenderKind } from '@affine/core/modules/pdf/workers/types';
import type { AttachmentBlockModel } from '@blocksuite/affine/blocks';
import { CollapseIcon, ExpandIcon } from '@blocksuite/icons/rc';
import { LiveData, useLiveData, useService } from '@toeverything/infra';
import clsx from 'clsx';
import { debounce } from 'lodash-es';
import type { ReactElement } from 'react';
Expand All @@ -18,8 +25,6 @@ import { Virtuoso } from 'react-virtuoso';

import * as styles from './styles.css';
import { getAttachmentBlob, renderItem } from './utils';
import type { DocInfo, MessageData, MessageDataType } from './worker/types';
import { MessageOp, RenderKind, State } from './worker/types';

type ItemProps = VirtuosoProps<null, undefined>;

Expand Down Expand Up @@ -100,22 +105,31 @@ interface ViewerProps {

export const Viewer = ({ model }: ViewerProps): ReactElement => {
const { showBoundary } = useErrorBoundary();
const [state, setState] = useState(State.Connecting);
const service = useService(PDFService);
const [worker, setWorker] = useState<PDFWorker | null>(null);
const docInfo = useLiveData(
useMemo(
() =>
worker
? worker.docInfo$
: new LiveData({
total: 0,
width: 1,
height: 1,
}),
[worker]
)
);
const [channel, setChannel] = useState<PDFChannel | null>(null);
const [cursor, setCursor] = useState(0);
const [viewportInfo, setViewportInfo] = useState({
dpi: window.devicePixelRatio,
width: 1,
height: 1,
});
const [docInfo, setDocInfo] = useState<DocInfo>({
total: 0,
width: 1,
height: 1,
});
const [cursor, setCursor] = useState(0);
const viewerRef = useRef<HTMLDivElement | null>(null);
const scrollerRef = useRef<HTMLElement | null>(null);
const scrollerHandleRef = useRef<VirtuosoHandle | null>(null);
const workerRef = useRef<Worker | null>(null);

const [mainVisibleRange, setMainVisibleRange] = useState({
startIndex: 0,
Expand All @@ -130,22 +144,6 @@ export const Viewer = ({ model }: ViewerProps): ReactElement => {
endIndex: 0,
});

const post = useCallback(
<T extends MessageOp>(
type: T,
data?: MessageDataType[T],
transfers?: Transferable[]
) => {
const message = { type, [type]: data };
if (transfers?.length) {
workerRef.current?.postMessage(message, transfers);
return;
}
workerRef.current?.postMessage(message);
},
[workerRef]
);

const render = useCallback(
(id: number, kind: RenderKind, imageData: ImageData) => {
const isPage = kind === RenderKind.Page;
Expand Down Expand Up @@ -209,85 +207,35 @@ export const Viewer = ({ model }: ViewerProps): ReactElement => {
}, [viewerRef]);

useEffect(() => {
post(MessageOp.Render, {
range: mainVisibleRange,
kind: RenderKind.Page,
scale: 1 * viewportInfo.dpi,
});
}, [viewportInfo, mainVisibleRange, post]);
if (!channel) return;

useEffect(() => {
if (collapsed) return;
const { startIndex, endIndex } = mainVisibleRange;
const scale = 1 * viewportInfo.dpi;

post(MessageOp.Render, {
range: thumbnailsVisibleRange,
kind: RenderKind.Thumbnail,
scale: (THUMBNAIL_WIDTH / docInfo.width) * viewportInfo.dpi,
});
}, [collapsed, docInfo, viewportInfo, thumbnailsVisibleRange, post]);

useLayoutEffect(() => {
workerRef.current = new Worker(
/* webpackChunkName: "pdf.worker" */ new URL(
'./worker/worker.ts',
import.meta.url
)
);

async function process({ data }: MessageEvent<MessageData>) {
const { type } = data;

switch (type) {
case MessageOp.Init: {
setState(State.Connecting);
break;
}

case MessageOp.Inited: {
setState(State.Connected);
break;
}

case MessageOp.Opened: {
const info = data[type];
setDocInfo(o => ({ ...o, ...info }));
setState(State.Opened);
break;
}

case MessageOp.Rendered: {
const { index, kind, imageData } = data[type];
render(index, kind, imageData);
break;
}
}
for (let i = startIndex; i <= endIndex; i++) {
channel.post(MessageOp.Render, {
index: i,
scale,
kind: RenderKind.Page,
});
}
}, [viewportInfo, mainVisibleRange, channel]);

workerRef.current.addEventListener('message', event => {
process(event).catch(console.error);
});
useEffect(() => {
if (collapsed) return;
if (!channel) return;

return () => {
workerRef.current?.terminate();
};
}, [model, post, render]);
const { startIndex, endIndex } = thumbnailsVisibleRange;
const scale = (THUMBNAIL_WIDTH / docInfo.width) * viewportInfo.dpi;

useEffect(() => {
if (!model.sourceId) return;
if (state !== State.Connected) return;

getAttachmentBlob(model)
.then(blob => {
if (!blob) return;
return blob.arrayBuffer();
})
.then(buffer => {
if (!buffer) return;
setState(State.Opening);
post(MessageOp.Open, buffer, [buffer]);
})
.catch(showBoundary);
}, [showBoundary, state, post, model, docInfo]);
for (let i = startIndex; i <= endIndex; i++) {
channel.post(MessageOp.Render, {
index: i,
scale,
kind: RenderKind.Thumbnail,
});
}
}, [collapsed, docInfo, viewportInfo, thumbnailsVisibleRange, channel]);

const pageContent = useCallback(
(index: number) => {
Expand Down Expand Up @@ -348,10 +296,7 @@ export const Viewer = ({ model }: ViewerProps): ReactElement => {
const size = Math.min(5, docInfo.total);
const itemHeight = docInfo.height + 20;
const height = Math.ceil(size * itemHeight);
return {
top: height,
bottom: height,
};
return { top: height, bottom: height };
}, [docInfo]);

const mainStyle = useMemo(() => {
Expand All @@ -361,11 +306,47 @@ export const Viewer = ({ model }: ViewerProps): ReactElement => {
vh - 60 - 24 - 24 - 2 - 8,
t * THUMBNAIL_WIDTH * (h / w) + (t - 1) * 12
);
return {
height: `${height}px`,
};
return { height: `${height}px` };
}, [docInfo, viewportInfo]);

useLayoutEffect(() => {
const { worker, release } = service.get(model.id);

setWorker(worker);

const channel = worker.channel();
channel
.on(({ index, kind, imageData }) => render(index, kind, imageData))
.start();

setChannel(channel);

const disposables = worker.on({
ready: () => {
if (worker.docInfo$.value.total) {
return;
}

getAttachmentBlob(model)
.then(blob => {
if (!blob) return;
return blob.arrayBuffer();
})
.then(buffer => {
if (!buffer) return;
worker.open(buffer);
})
.catch(showBoundary);
},
});

return () => {
channel.dispose();
disposables[Symbol.dispose]();
release();
};
}, [showBoundary, render, service, model]);

return (
<div
data-testid="attachment-viewer"
Expand Down

This file was deleted.

Loading

0 comments on commit 7f5c1ad

Please sign in to comment.