mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-02 06:39:46 +08:00
feat(core): add editor commanads (#4514)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
This commit is contained in:
@@ -23,12 +23,15 @@ import {
|
||||
usePageMetaHelper,
|
||||
} from '@toeverything/hooks/use-block-suite-page-meta';
|
||||
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
|
||||
import { useAtom, useSetAtom } from 'jotai';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
||||
|
||||
import { pageSettingFamily, setPageModeAtom } from '../../../atoms';
|
||||
import { setPageModeAtom } from '../../../atoms';
|
||||
import { currentModeAtom } from '../../../atoms/mode';
|
||||
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
|
||||
import { useExportPage } from '../../../hooks/affine/use-export-page';
|
||||
import { useTrashModalHelper } from '../../../hooks/affine/use-trash-modal-helper';
|
||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
|
||||
import { toast } from '../../../utils';
|
||||
@@ -43,72 +46,81 @@ type PageMenuProps = {
|
||||
export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const ref = useRef(null);
|
||||
const { openPage } = useNavigateHelper();
|
||||
|
||||
// fixme(himself65): remove these hooks ASAP
|
||||
const [workspace] = useCurrentWorkspace();
|
||||
|
||||
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
|
||||
const currentPage = blockSuiteWorkspace.getPage(pageId);
|
||||
assertExists(currentPage);
|
||||
|
||||
const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
|
||||
meta => meta.id === pageId
|
||||
) as PageMeta;
|
||||
const [setting, setSetting] = useAtom(pageSettingFamily(pageId));
|
||||
const mode = setting?.mode ?? 'page';
|
||||
|
||||
const currentMode = useAtomValue(currentModeAtom);
|
||||
const favorite = pageMeta.favorite ?? false;
|
||||
|
||||
const { setPageMeta, setPageTitle } = usePageMetaHelper(blockSuiteWorkspace);
|
||||
const [openConfirm, setOpenConfirm] = useState(false);
|
||||
const { removeToTrash } = useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||
const { togglePageMode, toggleFavorite } =
|
||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||
const { importFile } = usePageHelper(blockSuiteWorkspace);
|
||||
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
||||
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
||||
|
||||
const handleOpenTrashModal = useCallback(() => {
|
||||
setTrashModal({
|
||||
open: true,
|
||||
pageId,
|
||||
pageTitle: pageMeta.title,
|
||||
});
|
||||
}, [pageId, pageMeta.title, setTrashModal]);
|
||||
|
||||
const handleFavorite = useCallback(() => {
|
||||
setPageMeta(pageId, { favorite: !favorite });
|
||||
toggleFavorite(pageId);
|
||||
toast(
|
||||
favorite
|
||||
? t['com.affine.toastMessage.removedFavorites']()
|
||||
: t['com.affine.toastMessage.addedFavorites']()
|
||||
);
|
||||
}, [favorite, pageId, setPageMeta, t]);
|
||||
}, [favorite, pageId, t, toggleFavorite]);
|
||||
const handleSwitchMode = useCallback(() => {
|
||||
setSetting(setting => ({
|
||||
mode: setting?.mode === 'page' ? 'edgeless' : 'page',
|
||||
}));
|
||||
togglePageMode(pageId);
|
||||
toast(
|
||||
mode === 'page'
|
||||
currentMode === 'page'
|
||||
? t['com.affine.toastMessage.edgelessMode']()
|
||||
: t['com.affine.toastMessage.pageMode']()
|
||||
);
|
||||
}, [mode, setSetting, t]);
|
||||
const handleOnConfirm = useCallback(() => {
|
||||
removeToTrash(pageId);
|
||||
toast(t['com.affine.toastMessage.movedTrash']());
|
||||
setOpenConfirm(false);
|
||||
}, [pageId, removeToTrash, t]);
|
||||
}, [currentMode, pageId, t, togglePageMode]);
|
||||
const menuItemStyle = {
|
||||
padding: '4px 12px',
|
||||
transition: 'all 0.3s',
|
||||
};
|
||||
const { openPage } = useNavigateHelper();
|
||||
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
|
||||
|
||||
const exportHandler = useExportPage(currentPage);
|
||||
const setPageMode = useSetAtom(setPageModeAtom);
|
||||
|
||||
const duplicate = useCallback(async () => {
|
||||
const currentPage = blockSuiteWorkspace.getPage(pageId);
|
||||
assertExists(currentPage);
|
||||
const currentPageMeta = currentPage.meta;
|
||||
const newPage = createPage();
|
||||
await newPage.waitForLoaded();
|
||||
|
||||
const update = encodeStateAsUpdate(currentPage.spaceDoc);
|
||||
applyUpdate(newPage.spaceDoc, update);
|
||||
|
||||
setPageMeta(newPage.id, {
|
||||
tags: currentPageMeta.tags,
|
||||
favorite: currentPageMeta.favorite,
|
||||
});
|
||||
setPageMode(newPage.id, mode);
|
||||
setPageMode(newPage.id, currentMode);
|
||||
setPageTitle(newPage.id, `${currentPageMeta.title}(1)`);
|
||||
openPage(blockSuiteWorkspace.id, newPage.id);
|
||||
}, [
|
||||
blockSuiteWorkspace,
|
||||
blockSuiteWorkspace.id,
|
||||
createPage,
|
||||
mode,
|
||||
currentMode,
|
||||
currentPage.meta,
|
||||
currentPage.spaceDoc,
|
||||
openPage,
|
||||
pageId,
|
||||
setPageMeta,
|
||||
setPageMode,
|
||||
setPageTitle,
|
||||
@@ -130,7 +142,7 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
{mode === 'page' ? <EdgelessIcon /> : <PageIcon />}
|
||||
{currentMode === 'page' ? <EdgelessIcon /> : <PageIcon />}
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-edgeless"
|
||||
@@ -138,7 +150,7 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
|
||||
style={menuItemStyle}
|
||||
>
|
||||
{t['Convert to ']()}
|
||||
{mode === 'page'
|
||||
{currentMode === 'page'
|
||||
? t['com.affine.pageMode.edgeless']()
|
||||
: t['com.affine.pageMode.page']()}
|
||||
</MenuItem>
|
||||
@@ -194,13 +206,11 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
|
||||
>
|
||||
{t['Import']()}
|
||||
</MenuItem>
|
||||
<Export />
|
||||
<Export exportHandler={exportHandler} />
|
||||
<MenuSeparator />
|
||||
<MoveToTrash
|
||||
data-testid="editor-option-menu-delete"
|
||||
onSelect={() => {
|
||||
setOpenConfirm(true);
|
||||
}}
|
||||
onSelect={handleOpenTrashModal}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
@@ -218,12 +228,6 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
|
||||
>
|
||||
<HeaderDropDownButton />
|
||||
</Menu>
|
||||
<MoveToTrash.ConfirmModal
|
||||
open={openConfirm}
|
||||
title={pageMeta.title}
|
||||
onConfirm={handleOnConfirm}
|
||||
onOpenChange={setOpenConfirm}
|
||||
/>
|
||||
</FlexWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -2,11 +2,12 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import { Tooltip } from '@toeverything/components/tooltip';
|
||||
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
|
||||
import { pageSettingFamily } from '../../../atoms';
|
||||
import { currentModeAtom } from '../../../atoms/mode';
|
||||
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
|
||||
import type { BlockSuiteWorkspace } from '../../../shared';
|
||||
import { toast } from '../../../utils';
|
||||
import { StyledEditorModeSwitch, StyledKeyboardItem } from './style';
|
||||
@@ -34,14 +35,17 @@ export const EditorModeSwitch = ({
|
||||
blockSuiteWorkspace,
|
||||
pageId,
|
||||
}: EditorModeSwitchProps) => {
|
||||
const [setting, setSetting] = useAtom(pageSettingFamily(pageId));
|
||||
const currentMode = setting?.mode ?? 'page';
|
||||
const t = useAFFiNEI18N();
|
||||
const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
|
||||
meta => meta.id === pageId
|
||||
);
|
||||
const t = useAFFiNEI18N();
|
||||
assertExists(pageMeta);
|
||||
const { trash } = pageMeta;
|
||||
|
||||
const { togglePageMode, switchToEdgelessMode, switchToPageMode } =
|
||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||
const currentMode = useAtomValue(currentModeAtom);
|
||||
|
||||
useEffect(() => {
|
||||
if (trash) {
|
||||
return;
|
||||
@@ -53,21 +57,33 @@ export const EditorModeSwitch = ({
|
||||
: e.key === 's' && e.altKey
|
||||
) {
|
||||
e.preventDefault();
|
||||
setSetting(setting => {
|
||||
if (setting?.mode !== 'page') {
|
||||
toast(t['com.affine.toastMessage.pageMode']());
|
||||
return { ...setting, mode: 'page' };
|
||||
} else {
|
||||
toast(t['com.affine.toastMessage.edgelessMode']());
|
||||
return { ...setting, mode: 'edgeless' };
|
||||
}
|
||||
});
|
||||
togglePageMode(pageId);
|
||||
toast(
|
||||
currentMode === 'page'
|
||||
? t['com.affine.toastMessage.edgelessMode']()
|
||||
: t['com.affine.toastMessage.pageMode']()
|
||||
);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', keydown, { capture: true });
|
||||
return () =>
|
||||
document.removeEventListener('keydown', keydown, { capture: true });
|
||||
}, [setSetting, t, trash]);
|
||||
}, [currentMode, pageId, t, togglePageMode, trash]);
|
||||
|
||||
const onSwitchToPageMode = useCallback(() => {
|
||||
if (currentMode === 'page') {
|
||||
return;
|
||||
}
|
||||
switchToPageMode(pageId);
|
||||
toast(t['com.affine.toastMessage.pageMode']());
|
||||
}, [currentMode, pageId, switchToPageMode, t]);
|
||||
const onSwitchToEdgelessMode = useCallback(() => {
|
||||
if (currentMode === 'edgeless') {
|
||||
return;
|
||||
}
|
||||
switchToEdgelessMode(pageId);
|
||||
toast(t['com.affine.toastMessage.edgelessMode']());
|
||||
}, [currentMode, pageId, switchToEdgelessMode, t]);
|
||||
|
||||
return (
|
||||
<Tooltip content={<TooltipContent />}>
|
||||
@@ -81,28 +97,14 @@ export const EditorModeSwitch = ({
|
||||
active={currentMode === 'page'}
|
||||
hide={trash && currentMode !== 'page'}
|
||||
trash={trash}
|
||||
onClick={() => {
|
||||
setSetting(setting => {
|
||||
if (setting?.mode !== 'page') {
|
||||
toast(t['com.affine.toastMessage.pageMode']());
|
||||
}
|
||||
return { ...setting, mode: 'page' };
|
||||
});
|
||||
}}
|
||||
onClick={onSwitchToPageMode}
|
||||
/>
|
||||
<EdgelessSwitchItem
|
||||
data-testid="switch-edgeless-mode-button"
|
||||
active={currentMode === 'edgeless'}
|
||||
hide={trash && currentMode !== 'edgeless'}
|
||||
trash={trash}
|
||||
onClick={() => {
|
||||
setSetting(setting => {
|
||||
if (setting?.mode !== 'edgeless') {
|
||||
toast(t['com.affine.toastMessage.edgelessMode']());
|
||||
}
|
||||
return { ...setting, mode: 'edgeless' };
|
||||
});
|
||||
}}
|
||||
onClick={onSwitchToEdgelessMode}
|
||||
/>
|
||||
</StyledEditorModeSwitch>
|
||||
</Tooltip>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Suspense, useCallback, useMemo } from 'react';
|
||||
|
||||
import { allPageModeSelectAtom } from '../../../atoms';
|
||||
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
|
||||
import { useTrashModalHelper } from '../../../hooks/affine/use-trash-modal-helper';
|
||||
import { useGetPageInfoById } from '../../../hooks/use-get-page-info';
|
||||
import type { BlockSuiteWorkspace } from '../../../shared';
|
||||
import { toast } from '../../../utils';
|
||||
@@ -134,7 +135,6 @@ export const BlockSuitePageList = ({
|
||||
const pageMetas = useBlockSuitePageMeta(blockSuiteWorkspace);
|
||||
const {
|
||||
toggleFavorite,
|
||||
removeToTrash,
|
||||
restoreFromTrash,
|
||||
permanentlyDeletePage,
|
||||
cancelPublicPage,
|
||||
@@ -144,6 +144,8 @@ export const BlockSuitePageList = ({
|
||||
usePageHelper(blockSuiteWorkspace);
|
||||
const t = useAFFiNEI18N();
|
||||
const getPageInfo = useGetPageInfoById(blockSuiteWorkspace);
|
||||
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
||||
|
||||
const tagOptionMap = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
@@ -246,10 +248,13 @@ export const BlockSuitePageList = ({
|
||||
onClickRestore: () => {
|
||||
restoreFromTrash(pageMeta.id);
|
||||
},
|
||||
removeToTrash: () => {
|
||||
removeToTrash(pageMeta.id);
|
||||
toast(t['com.affine.toastMessage.successfullyDeleted']());
|
||||
},
|
||||
removeToTrash: () =>
|
||||
setTrashModal({
|
||||
open: true,
|
||||
pageId: pageMeta.id,
|
||||
pageTitle: pageMeta.title,
|
||||
}),
|
||||
|
||||
onRestorePage: () => {
|
||||
restoreFromTrash(pageMeta.id);
|
||||
toast(
|
||||
|
||||
Reference in New Issue
Block a user