mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 01:09:54 +08:00
feat: connect pinboard and reference link (#1859)
This commit is contained in:
@@ -45,7 +45,7 @@ export const PinboardMenu = ({
|
|||||||
meta => !meta.trash && meta.title.includes(query)
|
meta => !meta.trash && meta.title.includes(query)
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleDrop } = usePinboardHandler({
|
const { dropPin } = usePinboardHandler({
|
||||||
blockSuiteWorkspace,
|
blockSuiteWorkspace,
|
||||||
metas,
|
metas,
|
||||||
});
|
});
|
||||||
@@ -54,7 +54,7 @@ export const PinboardMenu = ({
|
|||||||
(dropId: string) => {
|
(dropId: string) => {
|
||||||
const targetTitle = metas.find(m => m.id === dropId)?.title;
|
const targetTitle = metas.find(m => m.id === dropId)?.title;
|
||||||
|
|
||||||
handleDrop(currentMeta.id, dropId, {
|
dropPin(currentMeta.id, dropId, {
|
||||||
bottomLine: false,
|
bottomLine: false,
|
||||||
topLine: false,
|
topLine: false,
|
||||||
internal: true,
|
internal: true,
|
||||||
@@ -62,7 +62,7 @@ export const PinboardMenu = ({
|
|||||||
onPinboardClick?.({ dragId: currentMeta.id, dropId });
|
onPinboardClick?.({ dragId: currentMeta.id, dropId });
|
||||||
toast(`Moved "${currentMeta.title}" to "${targetTitle}"`);
|
toast(`Moved "${currentMeta.title}" to "${targetTitle}"`);
|
||||||
},
|
},
|
||||||
[currentMeta.id, currentMeta.title, handleDrop, metas, onPinboardClick]
|
[currentMeta.id, currentMeta.title, dropPin, metas, onPinboardClick]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data } = usePinboardData({
|
const { data } = usePinboardData({
|
||||||
|
|||||||
@@ -80,7 +80,13 @@ export const PageDetailEditor: React.FC<PageDetailEditorProps> = ({
|
|||||||
},
|
},
|
||||||
[onInit, setEditor]
|
[onInit, setEditor]
|
||||||
)}
|
)}
|
||||||
onLoad={onLoad}
|
onLoad={useCallback(
|
||||||
|
(page: Page, editor: EditorContainer) => {
|
||||||
|
setEditor(editor);
|
||||||
|
onLoad?.(page, editor);
|
||||||
|
},
|
||||||
|
[onLoad, setEditor]
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export const Pinboard = ({
|
|||||||
showOperationButton: true,
|
showOperationButton: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { handleAdd, handleDelete, handleDrop } = usePinboardHandler({
|
const { addPin, deletePin, dropPin } = usePinboardHandler({
|
||||||
blockSuiteWorkspace: blockSuiteWorkspace,
|
blockSuiteWorkspace: blockSuiteWorkspace,
|
||||||
metas: allMetas,
|
metas: allMetas,
|
||||||
onAdd,
|
onAdd,
|
||||||
@@ -54,9 +54,9 @@ export const Pinboard = ({
|
|||||||
<div data-testid="sidebar-pinboard-container">
|
<div data-testid="sidebar-pinboard-container">
|
||||||
<TreeView
|
<TreeView
|
||||||
data={data}
|
data={data}
|
||||||
onAdd={handleAdd}
|
onAdd={addPin}
|
||||||
onDelete={handleDelete}
|
onDelete={deletePin}
|
||||||
onDrop={handleDrop}
|
onDrop={dropPin}
|
||||||
indent={16}
|
indent={16}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { useAtomValue } from 'jotai';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { currentEditorAtom } from '../../atoms';
|
||||||
|
|
||||||
|
export function useReferenceLink(props?: {
|
||||||
|
pageLinkClicked?: (params: { pageId: string }) => void;
|
||||||
|
subpageLinked?: (params: { pageId: string }) => void;
|
||||||
|
subpageUnlinked?: (params: { pageId: string }) => void;
|
||||||
|
}) {
|
||||||
|
const { pageLinkClicked, subpageLinked, subpageUnlinked } = props ?? {};
|
||||||
|
const editor = useAtomValue(currentEditorAtom);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkClickedDisposable = editor.slots.pageLinkClicked.on(
|
||||||
|
({ pageId }) => {
|
||||||
|
pageLinkClicked?.({ pageId });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const subpageLinkedDisposable = editor.slots.subpageLinked.on(
|
||||||
|
({ pageId }) => {
|
||||||
|
subpageLinked?.({ pageId });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const subpageUnlinkedDisposable = editor.slots.subpageUnlinked.on(
|
||||||
|
({ pageId }) => {
|
||||||
|
subpageUnlinked?.({ pageId });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
linkClickedDisposable.dispose();
|
||||||
|
subpageLinkedDisposable.dispose();
|
||||||
|
subpageUnlinkedDisposable.dispose();
|
||||||
|
};
|
||||||
|
}, [editor, pageLinkClicked, subpageLinked, subpageUnlinked]);
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import { useCallback } from 'react';
|
|||||||
import type { BlockSuiteWorkspace } from '../shared';
|
import type { BlockSuiteWorkspace } from '../shared';
|
||||||
import { useBlockSuiteWorkspaceHelper } from './use-blocksuite-workspace-helper';
|
import { useBlockSuiteWorkspaceHelper } from './use-blocksuite-workspace-helper';
|
||||||
import { usePageMetaHelper } from './use-page-meta';
|
import { usePageMetaHelper } from './use-page-meta';
|
||||||
import type { NodeRenderProps, PinboardNode } from './use-pinboard-data';
|
import type { NodeRenderProps } from './use-pinboard-data';
|
||||||
|
|
||||||
const logger = new DebugLogger('pinboard');
|
const logger = new DebugLogger('pinboard');
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ export function usePinboardHandler({
|
|||||||
onDelete,
|
onDelete,
|
||||||
onDrop,
|
onDrop,
|
||||||
}: {
|
}: {
|
||||||
blockSuiteWorkspace: BlockSuiteWorkspace;
|
blockSuiteWorkspace: BlockSuiteWorkspace | null;
|
||||||
metas: PageMeta[];
|
metas: PageMeta[];
|
||||||
onAdd?: (addedId: string, parentId: string) => void;
|
onAdd?: (addedId: string, parentId: string) => void;
|
||||||
onDelete?: TreeViewProps<NodeRenderProps>['onDelete'];
|
onDelete?: TreeViewProps<NodeRenderProps>['onDelete'];
|
||||||
@@ -34,17 +34,43 @@ export function usePinboardHandler({
|
|||||||
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
||||||
const { getPageMeta, setPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
|
const { getPageMeta, setPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
|
||||||
|
|
||||||
const handleAdd = useCallback(
|
// Just need handle add operation, delete check is handled in blockSuite's reference link
|
||||||
(node: PinboardNode) => {
|
const addReferenceLink = useCallback(
|
||||||
const id = nanoid();
|
(pageId: string, referenceId: string) => {
|
||||||
createPage(id, node.id);
|
const page = blockSuiteWorkspace?.getPage(pageId);
|
||||||
onAdd?.(id, node.id);
|
if (!page) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const text = page.Text.fromDelta([
|
||||||
|
{
|
||||||
|
insert: ' ',
|
||||||
|
attributes: {
|
||||||
|
reference: {
|
||||||
|
type: 'Subpage',
|
||||||
|
pageId: referenceId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const [frame] = page.getBlockByFlavour('affine:frame');
|
||||||
|
|
||||||
|
frame && page.addBlock('affine:paragraph', { text }, frame.id);
|
||||||
},
|
},
|
||||||
[createPage, onAdd]
|
[blockSuiteWorkspace]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDelete = useCallback(
|
const addPin = useCallback(
|
||||||
(node: PinboardNode) => {
|
(parentId: string) => {
|
||||||
|
const id = nanoid();
|
||||||
|
createPage(id, parentId);
|
||||||
|
onAdd?.(id, parentId);
|
||||||
|
addReferenceLink(parentId, id);
|
||||||
|
},
|
||||||
|
[addReferenceLink, createPage, onAdd]
|
||||||
|
);
|
||||||
|
|
||||||
|
const deletePin = useCallback(
|
||||||
|
(deleteId: string) => {
|
||||||
const removeToTrash = (currentMeta: PageMeta) => {
|
const removeToTrash = (currentMeta: PageMeta) => {
|
||||||
const { subpageIds = [] } = currentMeta;
|
const { subpageIds = [] } = currentMeta;
|
||||||
setPageMeta(currentMeta.id, {
|
setPageMeta(currentMeta.id, {
|
||||||
@@ -52,17 +78,17 @@ export function usePinboardHandler({
|
|||||||
trashDate: +new Date(),
|
trashDate: +new Date(),
|
||||||
});
|
});
|
||||||
subpageIds.forEach(id => {
|
subpageIds.forEach(id => {
|
||||||
const subcurrentMeta = getPageMeta(id);
|
const subCurrentMeta = getPageMeta(id);
|
||||||
subcurrentMeta && removeToTrash(subcurrentMeta);
|
subCurrentMeta && removeToTrash(subCurrentMeta);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
removeToTrash(metas.find(m => m.id === node.id)!);
|
removeToTrash(metas.find(m => m.id === deleteId)!);
|
||||||
onDelete?.(node);
|
onDelete?.(deleteId);
|
||||||
},
|
},
|
||||||
[metas, getPageMeta, onDelete, setPageMeta]
|
[metas, getPageMeta, onDelete, setPageMeta]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDrop = useCallback(
|
const dropPin = useCallback(
|
||||||
(
|
(
|
||||||
dragId: string,
|
dragId: string,
|
||||||
dropId: string,
|
dropId: string,
|
||||||
@@ -96,7 +122,7 @@ export function usePinboardHandler({
|
|||||||
|
|
||||||
const dropParentMeta = metas.find(m => m.subpageIds?.includes(dropId));
|
const dropParentMeta = metas.find(m => m.subpageIds?.includes(dropId));
|
||||||
if (dropParentMeta?.id === dragParentMeta?.id) {
|
if (dropParentMeta?.id === dragParentMeta?.id) {
|
||||||
// same parent
|
// same parent, resort node
|
||||||
const newSubpageIds = [...(dragParentMeta?.subpageIds ?? [])];
|
const newSubpageIds = [...(dragParentMeta?.subpageIds ?? [])];
|
||||||
const deleteIndex = newSubpageIds.findIndex(id => id === dragId);
|
const deleteIndex = newSubpageIds.findIndex(id => id === dragId);
|
||||||
newSubpageIds.splice(deleteIndex, 1);
|
newSubpageIds.splice(deleteIndex, 1);
|
||||||
@@ -109,6 +135,7 @@ export function usePinboardHandler({
|
|||||||
});
|
});
|
||||||
return onDrop?.(dragId, dropId, position);
|
return onDrop?.(dragId, dropId, position);
|
||||||
}
|
}
|
||||||
|
// Old parent will delete drag node, new parent will be added
|
||||||
const newDragParentSubpageIds = [...(dragParentMeta?.subpageIds ?? [])];
|
const newDragParentSubpageIds = [...(dragParentMeta?.subpageIds ?? [])];
|
||||||
const deleteIndex = newDragParentSubpageIds.findIndex(
|
const deleteIndex = newDragParentSubpageIds.findIndex(
|
||||||
id => id === dragId
|
id => id === dragId
|
||||||
@@ -127,6 +154,7 @@ export function usePinboardHandler({
|
|||||||
setPageMeta(dropParentMeta.id, {
|
setPageMeta(dropParentMeta.id, {
|
||||||
subpageIds: newDropParentSubpageIds,
|
subpageIds: newDropParentSubpageIds,
|
||||||
});
|
});
|
||||||
|
dropParentMeta && addReferenceLink(dropParentMeta.id, dragId);
|
||||||
return onDrop?.(dragId, dropId, position);
|
return onDrop?.(dragId, dropId, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,14 +177,15 @@ export function usePinboardHandler({
|
|||||||
setPageMeta(dropMeta.id, {
|
setPageMeta(dropMeta.id, {
|
||||||
subpageIds: newSubpageIds,
|
subpageIds: newSubpageIds,
|
||||||
});
|
});
|
||||||
|
addReferenceLink(dropMeta.id, dragId);
|
||||||
},
|
},
|
||||||
[metas, onDrop, setPageMeta]
|
[addReferenceLink, metas, onDrop, setPageMeta]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleDrop,
|
dropPin,
|
||||||
handleAdd,
|
addPin,
|
||||||
handleDelete,
|
deletePin,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { NextRouter } from 'next/router';
|
import type { NextRouter } from 'next/router';
|
||||||
import { useMemo } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import type { WorkspaceSubPath } from '../shared';
|
import type { WorkspaceSubPath } from '../shared';
|
||||||
|
|
||||||
@@ -9,47 +9,70 @@ export const enum RouteLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useRouterHelper(router: NextRouter) {
|
export function useRouterHelper(router: NextRouter) {
|
||||||
return useMemo(
|
const jumpToPage = useCallback(
|
||||||
() => ({
|
(
|
||||||
jumpToPage: (
|
workspaceId: string,
|
||||||
workspaceId: string,
|
pageId: string,
|
||||||
pageId: string,
|
logic: RouteLogic = RouteLogic.PUSH
|
||||||
logic: RouteLogic = RouteLogic.PUSH
|
) => {
|
||||||
) => {
|
return router[logic]({
|
||||||
return router[logic]({
|
pathname: `/workspace/[workspaceId]/[pageId]`,
|
||||||
pathname: `/workspace/[workspaceId]/[pageId]`,
|
query: {
|
||||||
query: {
|
workspaceId,
|
||||||
workspaceId,
|
pageId,
|
||||||
pageId,
|
},
|
||||||
},
|
});
|
||||||
});
|
},
|
||||||
},
|
|
||||||
jumpToPublicWorkspacePage: (
|
|
||||||
workspaceId: string,
|
|
||||||
pageId: string,
|
|
||||||
logic: RouteLogic = RouteLogic.PUSH
|
|
||||||
) => {
|
|
||||||
return router[logic]({
|
|
||||||
pathname: `/public-workspace/[workspaceId]/[pageId]`,
|
|
||||||
query: {
|
|
||||||
workspaceId,
|
|
||||||
pageId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
jumpToSubPath: (
|
|
||||||
workspaceId: string,
|
|
||||||
subPath: WorkspaceSubPath,
|
|
||||||
logic: RouteLogic = RouteLogic.PUSH
|
|
||||||
): Promise<boolean> => {
|
|
||||||
return router[logic]({
|
|
||||||
pathname: `/workspace/[workspaceId]/${subPath}`,
|
|
||||||
query: {
|
|
||||||
workspaceId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[router]
|
[router]
|
||||||
);
|
);
|
||||||
|
const jumpToPublicWorkspacePage = useCallback(
|
||||||
|
(
|
||||||
|
workspaceId: string,
|
||||||
|
pageId: string,
|
||||||
|
logic: RouteLogic = RouteLogic.PUSH
|
||||||
|
) => {
|
||||||
|
return router[logic]({
|
||||||
|
pathname: `/public-workspace/[workspaceId]/[pageId]`,
|
||||||
|
query: {
|
||||||
|
workspaceId,
|
||||||
|
pageId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
const jumpToSubPath = useCallback(
|
||||||
|
(
|
||||||
|
workspaceId: string,
|
||||||
|
subPath: WorkspaceSubPath,
|
||||||
|
logic: RouteLogic = RouteLogic.PUSH
|
||||||
|
): Promise<boolean> => {
|
||||||
|
return router[logic]({
|
||||||
|
pathname: `/workspace/[workspaceId]/${subPath}`,
|
||||||
|
query: {
|
||||||
|
workspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
const openPage = useCallback(
|
||||||
|
(workspaceId: string, pageId: string) => {
|
||||||
|
const isPublicWorkspace =
|
||||||
|
router.pathname.split('/')[1] === 'public-workspace';
|
||||||
|
if (isPublicWorkspace) {
|
||||||
|
return jumpToPublicWorkspacePage(workspaceId, pageId);
|
||||||
|
} else {
|
||||||
|
return jumpToPage(workspaceId, pageId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[jumpToPage, jumpToPublicWorkspacePage, router.pathname]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
jumpToPage,
|
||||||
|
jumpToPublicWorkspacePage,
|
||||||
|
jumpToSubPath,
|
||||||
|
openPage,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}, [currentWorkspace]);
|
}, [currentWorkspace]);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { jumpToPage, jumpToPublicWorkspacePage } = useRouterHelper(router);
|
const { openPage } = useRouterHelper(router);
|
||||||
const [, setOpenWorkspacesModal] = useAtom(openWorkspacesModalAtom);
|
const [, setOpenWorkspacesModal] = useAtom(openWorkspacesModalAtom);
|
||||||
const helper = useBlockSuiteWorkspaceHelper(
|
const helper = useBlockSuiteWorkspaceHelper(
|
||||||
currentWorkspace?.blockSuiteWorkspace ?? null
|
currentWorkspace?.blockSuiteWorkspace ?? null
|
||||||
@@ -241,17 +241,6 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
|
|||||||
const isPublicWorkspace =
|
const isPublicWorkspace =
|
||||||
router.pathname.split('/')[1] === 'public-workspace';
|
router.pathname.split('/')[1] === 'public-workspace';
|
||||||
const title = useRouterTitle(router);
|
const title = useRouterTitle(router);
|
||||||
const handleOpenPage = useCallback(
|
|
||||||
(pageId: string) => {
|
|
||||||
assertExists(currentWorkspace);
|
|
||||||
if (isPublicWorkspace) {
|
|
||||||
jumpToPublicWorkspacePage(currentWorkspace.id, pageId);
|
|
||||||
} else {
|
|
||||||
jumpToPage(currentWorkspace.id, pageId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[currentWorkspace, isPublicWorkspace, jumpToPage, jumpToPublicWorkspacePage]
|
|
||||||
);
|
|
||||||
const handleCreatePage = useCallback(() => {
|
const handleCreatePage = useCallback(() => {
|
||||||
return helper.createPage(nanoid());
|
return helper.createPage(nanoid());
|
||||||
}, [helper]);
|
}, [helper]);
|
||||||
@@ -319,7 +308,13 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
|
|||||||
currentWorkspace={currentWorkspace}
|
currentWorkspace={currentWorkspace}
|
||||||
currentPageId={currentPageId}
|
currentPageId={currentPageId}
|
||||||
onOpenWorkspaceListModal={handleOpenWorkspaceListModal}
|
onOpenWorkspaceListModal={handleOpenWorkspaceListModal}
|
||||||
openPage={handleOpenPage}
|
openPage={useCallback(
|
||||||
|
(pageId: string) => {
|
||||||
|
assertExists(currentWorkspace);
|
||||||
|
return openPage(currentWorkspace.id, pageId);
|
||||||
|
},
|
||||||
|
[currentWorkspace, openPage]
|
||||||
|
)}
|
||||||
createPage={handleCreatePage}
|
createPage={handleCreatePage}
|
||||||
currentPath={router.asPath.split('?')[0]}
|
currentPath={router.asPath.split('?')[0]}
|
||||||
paths={isPublicWorkspace ? publicPathGenerator : pathGenerator}
|
paths={isPublicWorkspace ? publicPathGenerator : pathGenerator}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { Breadcrumbs, displayFlex, styled } from '@affine/component';
|
import { Breadcrumbs, displayFlex, styled } from '@affine/component';
|
||||||
import { useTranslation } from '@affine/i18n';
|
import { useTranslation } from '@affine/i18n';
|
||||||
import { PageIcon } from '@blocksuite/icons';
|
import { PageIcon } from '@blocksuite/icons';
|
||||||
|
import { assertExists } from '@blocksuite/store';
|
||||||
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-blocksuite-workspace-avatar-url';
|
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-blocksuite-workspace-avatar-url';
|
||||||
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-blocksuite-workspace-name';
|
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-blocksuite-workspace-name';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { Suspense, useEffect } from 'react';
|
import { Suspense, useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
publicPageBlockSuiteAtom,
|
publicPageBlockSuiteAtom,
|
||||||
@@ -18,6 +19,8 @@ import { QueryParamError } from '../../../components/affine/affine-error-eoundar
|
|||||||
import { PageDetailEditor } from '../../../components/page-detail-editor';
|
import { PageDetailEditor } from '../../../components/page-detail-editor';
|
||||||
import { WorkspaceAvatar } from '../../../components/pure/footer';
|
import { WorkspaceAvatar } from '../../../components/pure/footer';
|
||||||
import { PageLoading } from '../../../components/pure/loading';
|
import { PageLoading } from '../../../components/pure/loading';
|
||||||
|
import { useReferenceLink } from '../../../hooks/affine/use-reference-link';
|
||||||
|
import { useRouterHelper } from '../../../hooks/use-router-helper';
|
||||||
import { PublicWorkspaceLayout } from '../../../layouts/public-workspace-layout';
|
import { PublicWorkspaceLayout } from '../../../layouts/public-workspace-layout';
|
||||||
import type { NextPageWithLayout } from '../../../shared';
|
import type { NextPageWithLayout } from '../../../shared';
|
||||||
import { initPage } from '../../../utils';
|
import { initPage } from '../../../utils';
|
||||||
@@ -56,9 +59,21 @@ const PublicWorkspaceDetailPageInner: React.FC<{
|
|||||||
if (!blockSuiteWorkspace) {
|
if (!blockSuiteWorkspace) {
|
||||||
throw new Error('cannot find workspace');
|
throw new Error('cannot find workspace');
|
||||||
}
|
}
|
||||||
|
const router = useRouter();
|
||||||
|
const { openPage } = useRouterHelper(router);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
blockSuiteWorkspace.awarenessStore.setFlag('enable_block_hub', false);
|
blockSuiteWorkspace.awarenessStore.setFlag('enable_block_hub', false);
|
||||||
}, [blockSuiteWorkspace]);
|
}, [blockSuiteWorkspace]);
|
||||||
|
useReferenceLink({
|
||||||
|
pageLinkClicked: useCallback(
|
||||||
|
({ pageId }: { pageId: string }) => {
|
||||||
|
assertExists(currentWorkspace);
|
||||||
|
return openPage(blockSuiteWorkspace.id, pageId);
|
||||||
|
},
|
||||||
|
[blockSuiteWorkspace.id, openPage]
|
||||||
|
),
|
||||||
|
});
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [name] = useBlockSuiteWorkspaceName(blockSuiteWorkspace);
|
const [name] = useBlockSuiteWorkspaceName(blockSuiteWorkspace);
|
||||||
const [avatar] = useBlockSuiteWorkspaceAvatarUrl(blockSuiteWorkspace);
|
const [avatar] = useBlockSuiteWorkspaceAvatarUrl(blockSuiteWorkspace);
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
import { WorkspaceFlavour } from '@affine/workspace/type';
|
import { WorkspaceFlavour } from '@affine/workspace/type';
|
||||||
|
import { assertExists } from '@blocksuite/store';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import { Unreachable } from '../../../components/affine/affine-error-eoundary';
|
import { Unreachable } from '../../../components/affine/affine-error-eoundary';
|
||||||
import { PageLoading } from '../../../components/pure/loading';
|
import { PageLoading } from '../../../components/pure/loading';
|
||||||
|
import { useReferenceLink } from '../../../hooks/affine/use-reference-link';
|
||||||
import { useCurrentPageId } from '../../../hooks/current/use-current-page-id';
|
import { useCurrentPageId } from '../../../hooks/current/use-current-page-id';
|
||||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||||
|
import { usePageMeta } from '../../../hooks/use-page-meta';
|
||||||
|
import { usePinboardHandler } from '../../../hooks/use-pinboard-handler';
|
||||||
import { useSyncRecentViewsWithRouter } from '../../../hooks/use-recent-views';
|
import { useSyncRecentViewsWithRouter } from '../../../hooks/use-recent-views';
|
||||||
|
import { useRouterHelper } from '../../../hooks/use-router-helper';
|
||||||
import { useSyncRouterWithCurrentWorkspaceAndPage } from '../../../hooks/use-sync-router-with-current-workspace-and-page';
|
import { useSyncRouterWithCurrentWorkspaceAndPage } from '../../../hooks/use-sync-router-with-current-workspace-and-page';
|
||||||
import { WorkspaceLayout } from '../../../layouts';
|
import { WorkspaceLayout } from '../../../layouts';
|
||||||
import { WorkspacePlugins } from '../../../plugins';
|
import { WorkspacePlugins } from '../../../plugins';
|
||||||
@@ -21,12 +26,37 @@ function enableFullFlags(blockSuiteWorkspace: BlockSuiteWorkspace) {
|
|||||||
blockSuiteWorkspace.awarenessStore.setFlag('enable_block_hub', true);
|
blockSuiteWorkspace.awarenessStore.setFlag('enable_block_hub', true);
|
||||||
blockSuiteWorkspace.awarenessStore.setFlag('enable_drag_handle', true);
|
blockSuiteWorkspace.awarenessStore.setFlag('enable_drag_handle', true);
|
||||||
blockSuiteWorkspace.awarenessStore.setFlag('enable_surface', true);
|
blockSuiteWorkspace.awarenessStore.setFlag('enable_surface', true);
|
||||||
|
blockSuiteWorkspace.awarenessStore.setFlag('enable_linked_page', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const WorkspaceDetail: React.FC = () => {
|
const WorkspaceDetail: React.FC = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { openPage } = useRouterHelper(router);
|
||||||
const [pageId] = useCurrentPageId();
|
const [pageId] = useCurrentPageId();
|
||||||
const [currentWorkspace] = useCurrentWorkspace();
|
const [currentWorkspace] = useCurrentWorkspace();
|
||||||
useSyncRecentViewsWithRouter(useRouter());
|
|
||||||
|
const { deletePin } = usePinboardHandler({
|
||||||
|
blockSuiteWorkspace: currentWorkspace?.blockSuiteWorkspace ?? null,
|
||||||
|
metas: usePageMeta(currentWorkspace?.blockSuiteWorkspace ?? null ?? null),
|
||||||
|
});
|
||||||
|
|
||||||
|
useSyncRecentViewsWithRouter(router);
|
||||||
|
|
||||||
|
useReferenceLink({
|
||||||
|
pageLinkClicked: useCallback(
|
||||||
|
({ pageId }: { pageId: string }) => {
|
||||||
|
assertExists(currentWorkspace);
|
||||||
|
return openPage(currentWorkspace.id, pageId);
|
||||||
|
},
|
||||||
|
[currentWorkspace, openPage]
|
||||||
|
),
|
||||||
|
subpageUnlinked: useCallback(
|
||||||
|
({ pageId }: { pageId: string }) => {
|
||||||
|
deletePin(pageId);
|
||||||
|
},
|
||||||
|
[deletePin]
|
||||||
|
),
|
||||||
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentWorkspace) {
|
if (currentWorkspace) {
|
||||||
enableFullFlags(currentWorkspace.blockSuiteWorkspace);
|
enableFullFlags(currentWorkspace.blockSuiteWorkspace);
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ const TreeNodeItem = <RenderProps,>({
|
|||||||
<div ref={dropRef}>
|
<div ref={dropRef}>
|
||||||
{node.render?.(node, {
|
{node.render?.(node, {
|
||||||
isOver: isOver && canDrop,
|
isOver: isOver && canDrop,
|
||||||
onAdd: () => onAdd?.(node),
|
onAdd: () => onAdd?.(node.id),
|
||||||
onDelete: () => onDelete?.(node),
|
onDelete: () => onDelete?.(node.id),
|
||||||
collapsed,
|
collapsed,
|
||||||
setCollapsed,
|
setCollapsed,
|
||||||
isSelected: selectedId === node.id,
|
isSelected: selectedId === node.id,
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ type CommonProps<RenderProps = unknown> = {
|
|||||||
enableDnd?: boolean;
|
enableDnd?: boolean;
|
||||||
enableKeyboardSelection?: boolean;
|
enableKeyboardSelection?: boolean;
|
||||||
indent?: CSSProperties['paddingLeft'];
|
indent?: CSSProperties['paddingLeft'];
|
||||||
onAdd?: (node: Node<RenderProps>) => void;
|
onAdd?: (parentId: string) => void;
|
||||||
onDelete?: (node: Node<RenderProps>) => void;
|
onDelete?: (deleteId: string) => void;
|
||||||
onDrop?: OnDrop;
|
onDrop?: OnDrop;
|
||||||
// Only trigger when the enableKeyboardSelection is true
|
// Only trigger when the enableKeyboardSelection is true
|
||||||
onSelect?: (id: string) => void;
|
onSelect?: (id: string) => void;
|
||||||
|
|||||||
@@ -34,6 +34,16 @@ async function openPinboardPageOperationMenu(page: Page, id: string) {
|
|||||||
await node.getByTestId('pinboard-operation-button').click();
|
await node.getByTestId('pinboard-operation-button').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkIsChildInsertToParentInEditor(page: Page, pageId: string) {
|
||||||
|
await page
|
||||||
|
.getByTestId('sidebar-pinboard-container')
|
||||||
|
.getByTestId(`pinboard-${pageId}`)
|
||||||
|
.click();
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
const referenceLink = await page.locator('.affine-reference');
|
||||||
|
expect(referenceLink).not.toBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
test.describe('PinBoard interaction', () => {
|
test.describe('PinBoard interaction', () => {
|
||||||
test('Have initial root pinboard page when first in', async ({ page }) => {
|
test('Have initial root pinboard page when first in', async ({ page }) => {
|
||||||
const rootPinboardMeta = await initHomePageWithPinboard(page);
|
const rootPinboardMeta = await initHomePageWithPinboard(page);
|
||||||
@@ -77,6 +87,7 @@ test.describe('PinBoard interaction', () => {
|
|||||||
.getByTestId('[data-testid="sidebar-pinboard-container"]')
|
.getByTestId('[data-testid="sidebar-pinboard-container"]')
|
||||||
.getByTestId(`pinboard-${meta?.id}`)
|
.getByTestId(`pinboard-${meta?.id}`)
|
||||||
).not.toBeNull();
|
).not.toBeNull();
|
||||||
|
await checkIsChildInsertToParentInEditor(page, rootPinboardMeta?.id ?? '');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Add pinboard by sidebar operation menu', async ({ page }) => {
|
test('Add pinboard by sidebar operation menu', async ({ page }) => {
|
||||||
@@ -92,6 +103,8 @@ test.describe('PinBoard interaction', () => {
|
|||||||
.getByTestId('sidebar-pinboard-container')
|
.getByTestId('sidebar-pinboard-container')
|
||||||
.getByTestId(`pinboard-${newPageMeta?.id}`)
|
.getByTestId(`pinboard-${newPageMeta?.id}`)
|
||||||
).not.toBeNull();
|
).not.toBeNull();
|
||||||
|
console.log('rootPinboardMeta', rootPinboardMeta);
|
||||||
|
await checkIsChildInsertToParentInEditor(page, rootPinboardMeta?.id ?? '');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Move pinboard to another in sidebar', async ({ page }) => {
|
test('Move pinboard to another in sidebar', async ({ page }) => {
|
||||||
@@ -116,7 +129,6 @@ test.describe('PinBoard interaction', () => {
|
|||||||
await createPinboardPage(page, rootPinboardMeta?.id ?? '', 'test1');
|
await createPinboardPage(page, rootPinboardMeta?.id ?? '', 'test1');
|
||||||
await createPinboardPage(page, rootPinboardMeta?.id ?? '', 'test2');
|
await createPinboardPage(page, rootPinboardMeta?.id ?? '', 'test2');
|
||||||
const childMeta = (await getMetas(page)).find(m => m.title === 'test1');
|
const childMeta = (await getMetas(page)).find(m => m.title === 'test1');
|
||||||
const childMeta2 = (await getMetas(page)).find(m => m.title === 'test2');
|
|
||||||
|
|
||||||
await page.getByTestId('all-pages').click();
|
await page.getByTestId('all-pages').click();
|
||||||
await page
|
await page
|
||||||
|
|||||||
Reference in New Issue
Block a user