Skip to content

Commit

Permalink
feat: support opening in peek view
Browse files Browse the repository at this point in the history
  • Loading branch information
fundon committed Oct 23, 2024
1 parent 953aa36 commit 41d99e1
Show file tree
Hide file tree
Showing 12 changed files with 302 additions and 92 deletions.
37 changes: 20 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,22 +156,25 @@
"@reforged/maker-appimage/@electron-forge/maker-base": "7.5.0",
"macos-alias": "npm:@napi-rs/[email protected]",
"fs-xattr": "npm:@napi-rs/xattr@latest",
"@blocksuite/affine": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/all",
"@blocksuite/affine-block-embed": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/block-embed",
"@blocksuite/affine-block-list": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/block-list",
"@blocksuite/affine-block-paragraph": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/block-paragraph",
"@blocksuite/affine-block-surface": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/block-surface",
"@blocksuite/affine-components": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/components",
"@blocksuite/data-view": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/data-view",
"@blocksuite/affine-model": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/model",
"@blocksuite/affine-shared": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/shared",
"@blocksuite/affine-widget-scroll-anchoring": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/affine/widget-scroll-anchoring",
"@blocksuite/blocks": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/blocks",
"@blocksuite/block-std": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/framework/block-std",
"@blocksuite/global": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/framework/global",
"@blocksuite/inline": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/framework/inline",
"@blocksuite/store": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/framework/store",
"@blocksuite/sync": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/framework/sync",
"@blocksuite/presets": "portal:/C:/Users/xp/Documents/GitHub/blocksuite/packages/presets"
"@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/pdf-viewer-types": "portal:/Users/fundon/dev/toeverything/pdfium-builder/packages/types",
"@toeverything/pdfium": "portal:/Users/fundon/dev/toeverything/pdfium-builder/packages/pdfium"
}
}
2 changes: 2 additions & 0 deletions packages/frontend/component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@
"@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": "workspace:*",
"@toeverything/theme": "^1.0.11",
"@vanilla-extract/dynamic": "^2.1.0",
"check-password-strength": "^2.0.10",
"clsx": "^2.1.0",
"dayjs": "^1.11.10",
"file-type": "^19.1.0",
"jotai": "^2.8.0",
"lit": "^3.1.2",
"lodash-es": "^4.17.21",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { AttachmentBlockModel } from '@blocksuite/blocks';
import { ArrowDownBigIcon, PageIcon } from '@blocksuite/icons/rc';
import clsx from 'clsx';
import type { ReactElement } from 'react';
Expand All @@ -6,15 +7,17 @@ import { Button } from '../../ui/button';
import * as styles from './styles.css';

interface ErrorProps {
model: AttachmentBlockModel;
ext: string;
isPDF: boolean;
}

export const Error = (_: ErrorProps): ReactElement => {
export const Error = ({ ext }: ErrorProps): ReactElement => {
return (
<div className={clsx([styles.body, styles.error])}>
<PageIcon />
<h3 className={styles.errorTitle}>Unable to preview this file</h3>
<p className={styles.errorMessage}>.dmg file type not supported.</p>
<p className={styles.errorMessage}>.{ext} file type not supported.</p>
<div className={styles.errorBtns}>
<Button variant="primary" prefix={<ArrowDownBigIcon />}>
Download
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import { type ReactElement, useState } from 'react';
import { humanFileSize } from '@blocksuite/affine-shared/utils';
import type { AttachmentBlockModel } from '@blocksuite/blocks';
import { type ReactElement, useMemo } from 'react';

import { Error } from './error';
import * as styles from './styles.css';
import { Titlebar } from './titlebar';
import { Viewer } from './viewer';

export const AttachmentViewer = (): ReactElement => {
const [isPDF] = useState(true);
export type AttachmentViewerProps = {
model: AttachmentBlockModel;
};

export const AttachmentViewer = ({
model,
}: AttachmentViewerProps): ReactElement => {
const attachment = useMemo(() => {
const pieces = model.name.split('.');
const ext = pieces.pop() || '';
const name = pieces.join('.');
const isPDF = ext === 'pdf';
const filesize = humanFileSize(model.size);

return { model, name, ext, filesize, isPDF };
}, [model]);

return (
<div className={styles.viewerContainer}>
<Titlebar
id={'0'}
name={'AFFiNE'}
size={10}
unit={'MB'}
ext=".pdf"
zoom={100}
isPDF={isPDF}
/>
{isPDF ? <Viewer /> : <Error isPDF />}
<Titlebar {...attachment} />
{attachment.isPDF ? <Viewer /> : <Error {...attachment} />}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const viewerContainer = style({
display: 'flex',
flexDirection: 'column',
width: '100%',
height: '100%',
});

export const titlebar = style({
Expand Down Expand Up @@ -61,9 +62,6 @@ export const body = style({
backgroundSize: '20px 20px',
backgroundImage: `linear-gradient(${cssVarV2('button/grabber/default')} 1px, transparent 1px), linear-gradient(to right, ${cssVarV2('button/grabber/default')} 1px, transparent 1px)`,
},
'&.scrollable': {
overflowY: 'auto',
},
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { AttachmentBlockModel } from '@blocksuite/blocks';
import {
EditIcon,
LocalDataIcon,
Expand All @@ -12,16 +13,6 @@ import { IconButton } from '../../ui/button';
import { Menu, MenuItem } from '../../ui/menu';
import * as styles from './styles.css';

export interface TitlebarProps {
id: string;
name: string;
ext: string;
size: number;
unit: string;
zoom: number;
isPDF: boolean;
}

const items = [
{
name: 'Rename',
Expand All @@ -42,13 +33,22 @@ export const MenuItems = () =>
</MenuItem>
));

export interface TitlebarProps {
model: AttachmentBlockModel;
name: string;
ext: string;
filesize: string;
isPDF: boolean;
zoom?: number;
}

export const Titlebar = ({
model: _,
name,
ext,
size,
unit,
isPDF = false,
filesize,
zoom = 100,
isPDF = false,
}: TitlebarProps) => {
const [openMenu, setOpenMenu] = useState(false);

Expand All @@ -57,12 +57,9 @@ export const Titlebar = ({
<div className={styles.titlebarChild}>
<div className={styles.titlebarName}>
<div>{name}</div>
<span>{ext}</span>
</div>
<div>
{size}
{unit}
<span>.{ext}</span>
</div>
<div>{filesize}</div>
<IconButton icon={<LocalDataIcon />}></IconButton>
<Menu
items={<MenuItems />}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { fileTypeFromBuffer } from 'file-type';

async function _saveBufferToFile(url: string, filename: string) {
// given input url may not have correct mime type
const blob = await attachmentUrlToBlob(url);
if (!blob) {
return;
}

const blobUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobUrl;
a.download = filename;
document.body.append(a);
a.click();
a.remove();
URL.revokeObjectURL(blobUrl);
}

async function attachmentUrlToBlob(url: string): Promise<Blob | undefined> {
const buffer = await fetch(url).then(response => {
return response.arrayBuffer();
});

if (!buffer) {
console.warn('Could not get blob');
return;
}
try {
const type = await fileTypeFromBuffer(buffer);
if (!type) {
return;
}
const blob = new Blob([buffer], { type: type.mime });
return blob;
} catch (error) {
console.error('Error converting attachment to blob', error);
}
return;
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,83 @@
import { AttachmentViewer } from '@affine/component/attachment-viewer';
import type { ReactElement } from 'react';
import { type AttachmentBlockModel,matchFlavours } from '@blocksuite/blocks';

Check failure on line 2 in packages/frontend/core/src/desktop/pages/workspace/attachment/index.tsx

View workflow job for this annotation

GitHub Actions / Lint

'@blocksuite/blocks' should be listed in the project's dependencies. Run 'npm i -S @blocksuite/blocks' to add it
import {
type Doc,
DocsService,
FrameworkScope,
useService,
} from '@toeverything/infra';
import { type ReactElement, useEffect,useLayoutEffect, useState } from 'react';
import { useParams } from 'react-router-dom';

import {
// useIsActiveView,
ViewBody,
ViewHeader,
ViewIcon,
ViewTitle,
} from '../../../../modules/workbench';
import { PageNotFound } from '../../404';

const useLoadAttachment = (pageId?: string, attachmentId?: string) => {
const docsService = useService(DocsService);
const [doc, setDoc] = useState<Doc | null>(null);
const [model, setModel] = useState<AttachmentBlockModel | null>(null);

useLayoutEffect(() => {
if (!pageId) return;

const { doc, release } = docsService.open(pageId);

if (!doc.blockSuiteDoc.ready) {
doc.blockSuiteDoc.load();
}

setDoc(doc);

return () => {
release();
};
}, [docsService, pageId]);

useEffect(() => {
if (!doc) return;
if (!attachmentId) return;

const disposable = doc.blockSuiteDoc.slots.blockUpdated
.filter(
v =>
v.type === 'add' &&
v.id === attachmentId &&
matchFlavours(v, ['affine:attachment'])
)
.once(block => {
setModel(block.model as AttachmentBlockModel);
});

return () => {
disposable.dispose();
};
}, [doc, attachmentId]);

return { doc, model };
};

export const AttachmentPage = (): ReactElement => {
const params = useParams();
const { doc, model } = useLoadAttachment(params.pageId, params.attachmentId);

if (!doc || !model) {
return <PageNotFound noPermission />;
}

return (
<>
<ViewTitle title={'Attachment'} />
<ViewIcon icon={'pdf'} />
<ViewTitle title={model.name} />
<ViewIcon icon={model.type.endsWith('pdf') ? 'pdf' : 'attachment'} />
<ViewHeader></ViewHeader>
<ViewBody>
<AttachmentViewer />
<FrameworkScope scope={doc.scope}>
<AttachmentViewer model={model} />
</FrameworkScope>
</ViewBody>
</>
);
Expand Down
20 changes: 20 additions & 0 deletions packages/frontend/core/src/modules/peek-view/entities/peek-view.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { BlockComponent, EditorHost } from '@blocksuite/affine/block-std';
import type {
AttachmentBlockModel,
DocMode,
EmbedLinkedDocModel,
EmbedSyncedDocModel,
Expand Down Expand Up @@ -44,6 +45,12 @@ export type ImagePeekViewInfo = {
blockIds: [string];
};

export type AttachmentPeekViewInfo = {
type: 'attachment';
docId: string;
blockIds: [string];
};

export type AIChatBlockPeekViewInfo = {
type: 'ai-chat-block';
docId: string;
Expand All @@ -61,6 +68,7 @@ export type ActivePeekView = {
info:
| DocPeekViewInfo
| ImagePeekViewInfo
| AttachmentPeekViewInfo
| CustomTemplatePeekViewInfo
| AIChatBlockPeekViewInfo;
};
Expand All @@ -83,6 +91,12 @@ const isImageBlockModel = (
return blockModel.flavour === 'affine:image';
};

const isAttachmentBlockModel = (
blockModel: BlockModel
): blockModel is AttachmentBlockModel => {
return blockModel.flavour === 'affine:attachment';
};

const isSurfaceRefModel = (
blockModel: BlockModel
): blockModel is SurfaceRefBlockModel => {
Expand Down Expand Up @@ -147,6 +161,12 @@ function resolvePeekInfoFromPeekTarget(
xywh: refModel.xywh,
};
}
} else if (isAttachmentBlockModel(blockModel)) {
return {
type: 'attachment',
docId: blockModel.doc.id,
blockIds: [blockModel.id],
};
} else if (isImageBlockModel(blockModel)) {
return {
type: 'image',
Expand Down
Loading

0 comments on commit 41d99e1

Please sign in to comment.