import { AddPageButton, AppSidebar, appSidebarOpenAtom, AppUpdaterButton, CategoryDivider, MenuItem, MenuLinkItem, QuickSearchInput, SidebarContainer, SidebarScrollableContainer, } from '@affine/component/app-sidebar'; import { isDesktop } from '@affine/env/constant'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { DeleteTemporarilyIcon, FolderIcon, SettingsIcon, ShareIcon, } from '@blocksuite/icons'; import type { Page } from '@blocksuite/store'; import { useDroppable } from '@dnd-kit/core'; import { NoSsr } from '@mui/material'; import { useAtom } from 'jotai'; import type { ReactElement } from 'react'; import React, { useCallback, useEffect, useMemo } from 'react'; import { useHistoryAtom } from '../../atoms/history'; import { useAppSetting } from '../../atoms/settings'; import type { AllWorkspace } from '../../shared'; import FavoriteList from '../pure/workspace-slider-bar/favorite/favorite-list'; import { WorkspaceSelector } from '../pure/workspace-slider-bar/WorkspaceSelector'; export type RootAppSidebarProps = { isPublicWorkspace: boolean; onOpenQuickSearchModal: () => void; onOpenSettingModal: () => void; onOpenWorkspaceListModal: () => void; currentWorkspace: AllWorkspace | null; openPage: (pageId: string) => void; createPage: () => Page; currentPath: string; paths: { all: (workspaceId: string) => string; trash: (workspaceId: string) => string; setting: (workspaceId: string) => string; shared: (workspaceId: string) => string; }; }; const RouteMenuLinkItem = React.forwardRef< HTMLDivElement, { currentPath: string; // todo: pass through useRouter? path?: string | null; icon: ReactElement; children?: ReactElement; isDraggedOver?: boolean; } & React.HTMLAttributes >(({ currentPath, path, icon, children, isDraggedOver, ...props }, ref) => { // Force active style when a page is dragged over const active = isDraggedOver || currentPath === path; return ( {children} ); }); RouteMenuLinkItem.displayName = 'RouteMenuLinkItem'; // Unique droppable IDs export const DROPPABLE_SIDEBAR_TRASH = 'trash-folder'; /** * This is for the whole affine app sidebar. * This component wraps the app sidebar in `@affine/component` with logic and data. * * @todo(himself65): rewrite all styled component into @vanilla-extract/css */ export const RootAppSidebar = ({ currentWorkspace, openPage, createPage, currentPath, paths, onOpenQuickSearchModal, onOpenWorkspaceListModal, onOpenSettingModal, }: RootAppSidebarProps): ReactElement => { const currentWorkspaceId = currentWorkspace?.id || null; const [appSettings] = useAppSetting(); const blockSuiteWorkspace = currentWorkspace?.blockSuiteWorkspace; const t = useAFFiNEI18N(); const onClickNewPage = useCallback(async () => { const page = createPage(); openPage(page.id); }, [createPage, openPage]); // Listen to the "New Page" action from the menu useEffect(() => { if (isDesktop) { return window.events?.applicationMenu.onNewPageAction(onClickNewPage); } }, [onClickNewPage]); const [sidebarOpen, setSidebarOpen] = useAtom(appSidebarOpenAtom); useEffect(() => { if (isDesktop && typeof sidebarOpen === 'boolean') { window.apis?.ui.handleSidebarVisibilityChange(sidebarOpen).catch(err => { console.error(err); }); } }, [sidebarOpen]); useEffect(() => { const keydown = (e: KeyboardEvent) => { if ((e.key === '/' && e.metaKey) || (e.key === '/' && e.ctrlKey)) { setSidebarOpen(!sidebarOpen); } }; document.addEventListener('keydown', keydown, { capture: true }); return () => document.removeEventListener('keydown', keydown, { capture: true }); }, [sidebarOpen, setSidebarOpen]); const [history, setHistory] = useHistoryAtom(); const router = useMemo(() => { return { forward: () => { setHistory(true); }, back: () => { setHistory(false); }, history, }; }, [history, setHistory]); const trashDroppable = useDroppable({ id: DROPPABLE_SIDEBAR_TRASH, }); return ( <> } currentPath={currentPath} path={currentWorkspaceId && paths.all(currentWorkspaceId)} > {t['All pages']()} } currentPath={currentPath} path={currentWorkspaceId && paths.setting(currentWorkspaceId)} > {t['Settings']()} {runtimeConfig.enableNewSettingModal ? ( } onClick={onOpenSettingModal}> {t['Settings']()} NEW ) : null} {blockSuiteWorkspace && ( )} {runtimeConfig.enableLegacyCloud && (currentWorkspace?.flavour === WorkspaceFlavour.AFFINE && currentWorkspace.public ? ( } currentPath={currentPath} path={currentWorkspaceId && paths.setting(currentWorkspaceId)} > Published to web ) : ( } currentPath={currentPath} path={currentWorkspaceId && paths.shared(currentWorkspaceId)} > {t['Shared Pages']()} ))} } currentPath={currentPath} path={currentWorkspaceId && paths.trash(currentWorkspaceId)} > {t['Trash']()} {isDesktop && }
); };