refactor: lazy load workspaces (#3091)

(cherry picked from commit 283f0cd263)
This commit is contained in:
Alex Yang
2023-07-07 22:15:27 +08:00
parent e6530f5017
commit 1908d1a213
45 changed files with 446 additions and 750 deletions
@@ -11,7 +11,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import type { FC } from 'react';
import type { AffineOfficialWorkspace } from '../../../shared';
import { useWorkspace } from '../../../hooks/use-workspace';
import { DeleteLeaveWorkspace } from './delete-leave-workspace';
import { ExportPanel } from './export';
import { ProfilePanel } from './profile';
@@ -19,7 +19,7 @@ import { PublishPanel } from './publish';
import { StoragePanel } from './storage';
export type WorkspaceSettingDetailProps = {
workspace: AffineOfficialWorkspace;
workspaceId: string;
onDeleteWorkspace: (id: string) => Promise<void>;
onTransferWorkspace: <
From extends WorkspaceFlavour,
@@ -32,11 +32,12 @@ export type WorkspaceSettingDetailProps = {
};
export const WorkspaceSettingDetail: FC<WorkspaceSettingDetailProps> = ({
workspace,
workspaceId,
onDeleteWorkspace,
...props
}) => {
const t = useAFFiNEI18N();
const workspace = useWorkspace(workspaceId);
const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace);
return (
@@ -64,7 +64,10 @@ export const ProfilePanel: FC<{
<div className="camera-icon-wrapper">
<CameraIcon />
</div>
<WorkspaceAvatar size={56} workspace={workspace} />
<WorkspaceAvatar
size={56}
workspace={workspace.blockSuiteWorkspace}
/>
</>
</Upload>
</div>
@@ -18,13 +18,22 @@ import { TmpDisableAffineCloudModal } from '../tmp-disable-affine-cloud-modal';
import type { WorkspaceSettingDetailProps } from './index';
import * as style from './style.css';
export type PublishPanelProps = WorkspaceSettingDetailProps & {
export type PublishPanelProps = Omit<
WorkspaceSettingDetailProps,
'workspaceId'
> & {
workspace: AffineOfficialWorkspace;
};
export type PublishPanelLocalProps = WorkspaceSettingDetailProps & {
export type PublishPanelLocalProps = Omit<
WorkspaceSettingDetailProps,
'workspaceId'
> & {
workspace: LocalWorkspace;
};
export type PublishPanelAffineProps = WorkspaceSettingDetailProps & {
export type PublishPanelAffineProps = Omit<
WorkspaceSettingDetailProps,
'workspaceId'
> & {
workspace: AffineCloudWorkspace;
};
@@ -4,14 +4,13 @@ import {
} from '@affine/component/setting-components';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import { ContactWithUsIcon } from '@blocksuite/icons';
import type { PassiveDocProvider } from '@blocksuite/store';
import { noop } from 'foxact/noop';
import { useAtomValue } from 'jotai';
import type React from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
import { useWorkspaces } from '../../../hooks/use-workspaces';
import type { AllWorkspace } from '../../../shared';
import { AccountSetting } from './account-setting';
import {
@@ -26,70 +25,52 @@ import { WorkspaceSetting } from './workspace-setting';
type ActiveTab = GeneralSettingKeys | 'workspace' | 'account';
export type SettingProps = {
activeTab: ActiveTab;
workspace: AllWorkspace | null;
workspaceId: string | null;
onSettingClick: (params: {
activeTab: ActiveTab;
workspace: AllWorkspace | null;
workspaceId: string | null;
}) => void;
};
export const SettingModal: React.FC<SettingModalProps & SettingProps> = ({
open,
setOpen,
activeTab = 'appearance',
workspace = null,
workspaceId = null,
onSettingClick,
}) => {
const t = useAFFiNEI18N();
const workspaces = useWorkspaces();
const workspaces = useAtomValue(rootWorkspacesMetadataAtom);
const [currentWorkspace] = useCurrentWorkspace();
const generalSettingList = useGeneralSettingList();
const workspaceList = useMemo(() => {
return workspaces.filter(
({ flavour }) => flavour !== WorkspaceFlavour.PUBLIC
) as AllWorkspace[];
);
}, [workspaces]);
const onGeneralSettingClick = useCallback(
(key: GeneralSettingKeys) => {
onSettingClick({
activeTab: key,
workspace: null,
workspaceId: null,
});
},
[onSettingClick]
);
const onWorkspaceSettingClick = useCallback(
(workspace: AllWorkspace) => {
(workspaceId: string) => {
onSettingClick({
activeTab: 'workspace',
workspace,
workspaceId,
});
},
[onSettingClick]
);
const onAccountSettingClick = useCallback(() => {
onSettingClick({ activeTab: 'account', workspace: null });
onSettingClick({ activeTab: 'account', workspaceId: null });
}, [onSettingClick]);
useEffect(() => {
if (workspace && workspace !== currentWorkspace) {
const providers = workspace.blockSuiteWorkspace.providers.filter(
(provider): provider is PassiveDocProvider =>
'passive' in provider && provider.passive
);
providers.forEach(provider => {
provider.connect();
});
return () => {
providers.forEach(provider => {
provider.disconnect();
});
};
}
return noop;
}, [currentWorkspace, workspace]);
return (
<SettingModalBase open={open} setOpen={setOpen}>
<SettingSidebar
@@ -99,15 +80,15 @@ export const SettingModal: React.FC<SettingModalProps & SettingProps> = ({
workspaceList={workspaceList}
onWorkspaceSettingClick={onWorkspaceSettingClick}
selectedGeneralKey={activeTab}
selectedWorkspace={workspace}
selectedWorkspaceId={workspaceId}
onAccountSettingClick={onAccountSettingClick}
/>
<div className={settingContent}>
<div className="wrapper">
<div className="content">
{activeTab === 'workspace' && workspace ? (
<WorkspaceSetting key={workspace.id} workspace={workspace} />
{activeTab === 'workspace' && workspaceId ? (
<WorkspaceSetting key={workspaceId} workspaceId={workspaceId} />
) : null}
{generalSettingList.find(v => v.key === activeTab) ? (
<GeneralSetting generalKey={activeTab as GeneralSettingKeys} />
@@ -1,6 +1,8 @@
import { UserAvatar } from '@affine/component/user-avatar';
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
import { useStaticBlockSuiteWorkspace } from '@affine/workspace/utils';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import clsx from 'clsx';
@@ -24,17 +26,17 @@ export const SettingSidebar = ({
currentWorkspace,
workspaceList,
onWorkspaceSettingClick,
selectedWorkspace,
selectedWorkspaceId,
selectedGeneralKey,
onAccountSettingClick,
}: {
generalSettingList: GeneralSettingList;
onGeneralSettingClick: (key: GeneralSettingKeys) => void;
currentWorkspace: AllWorkspace;
workspaceList: AllWorkspace[];
onWorkspaceSettingClick: (workspace: AllWorkspace) => void;
workspaceList: RootWorkspaceMetadata[];
onWorkspaceSettingClick: (workspaceId: string) => void;
selectedWorkspace: AllWorkspace | null;
selectedWorkspaceId: string | null;
selectedGeneralKey: string | null;
onAccountSettingClick: () => void;
}) => {
@@ -72,12 +74,12 @@ export const SettingSidebar = ({
return (
<WorkspaceListItem
key={workspace.id}
workspace={workspace}
meta={workspace}
onClick={() => {
onWorkspaceSettingClick(workspace);
onWorkspaceSettingClick(workspace.id);
}}
isCurrent={workspace.id === currentWorkspace.id}
isActive={workspace.id === selectedWorkspace?.id}
isActive={workspace.id === selectedWorkspaceId}
/>
);
})}
@@ -107,19 +109,18 @@ export const SettingSidebar = ({
};
const WorkspaceListItem = ({
workspace,
meta,
onClick,
isCurrent,
isActive,
}: {
workspace: AllWorkspace;
meta: RootWorkspaceMetadata;
onClick: () => void;
isCurrent: boolean;
isActive: boolean;
}) => {
const [workspaceName] = useBlockSuiteWorkspaceName(
workspace.blockSuiteWorkspace ?? null
);
const workspace = useStaticBlockSuiteWorkspace(meta.id);
const [workspaceName] = useBlockSuiteWorkspaceName(workspace);
return (
<div
className={clsx(sidebarSelectItem, { active: isActive })}
@@ -1,16 +1,16 @@
import { Suspense, useCallback } from 'react';
import { getUIAdapter } from '../../../../adapters/workspace';
import { usePassiveWorkspaceEffect } from '../../../../hooks/current/use-current-workspace';
import { useOnTransformWorkspace } from '../../../../hooks/root/use-on-transform-workspace';
import { useWorkspace } from '../../../../hooks/use-workspace';
import { useAppHelper } from '../../../../hooks/use-workspaces';
import type { AllWorkspace } from '../../../../shared';
export const WorkspaceSetting = ({
workspace,
}: {
workspace: AllWorkspace;
}) => {
export const WorkspaceSetting = ({ workspaceId }: { workspaceId: string }) => {
const workspace = useWorkspace(workspaceId);
usePassiveWorkspaceEffect(workspace.blockSuiteWorkspace);
const helper = useAppHelper();
const { NewSettingsDetail } = getUIAdapter(workspace.flavour);
const onDeleteWorkspace = useCallback(
@@ -26,7 +26,7 @@ export const WorkspaceSetting = ({
<NewSettingsDetail
onTransformWorkspace={onTransformWorkspace}
onDeleteWorkspace={onDeleteWorkspace}
currentWorkspace={workspace}
currentWorkspaceId={workspaceId}
/>
</Suspense>
);
+9 -12
View File
@@ -7,7 +7,7 @@ import {
import { rootBlockHubAtom } from '@affine/workspace/atom';
import type { EditorContainer } from '@blocksuite/editor';
import { assertExists } from '@blocksuite/global/utils';
import type { Page } from '@blocksuite/store';
import type { Page, Workspace } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page';
import { useBlockSuiteWorkspacePageTitle } from '@toeverything/hooks/use-block-suite-workspace-page-title';
@@ -28,14 +28,13 @@ import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import { pageSettingFamily } from '../atoms';
import { contentLayoutAtom } from '../atoms/layout';
import { useAppSetting } from '../atoms/settings';
import type { AffineOfficialWorkspace } from '../shared';
import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor';
import { editor } from './page-detail-editor.css';
import { pluginContainer } from './page-detail-editor.css';
export type PageDetailEditorProps = {
isPublic?: boolean;
workspace: AffineOfficialWorkspace;
workspace: Workspace;
pageId: string;
onInit: (page: Page, editor: Readonly<EditorContainer>) => void;
onLoad?: (page: Page, editor: EditorContainer) => () => void;
@@ -53,12 +52,11 @@ const EditorWrapper = memo(function EditorWrapper({
() => Object.values(affinePluginsMap),
[affinePluginsMap]
);
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
const page = useBlockSuiteWorkspacePage(blockSuiteWorkspace, pageId);
const page = useBlockSuiteWorkspacePage(workspace, pageId);
if (!page) {
throw new PageNotFoundError(blockSuiteWorkspace, pageId);
throw new PageNotFoundError(workspace, pageId);
}
const meta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
const meta = useBlockSuitePageMeta(workspace).find(
meta => meta.id === pageId
);
const pageSettingAtom = pageSettingFamily(pageId);
@@ -77,7 +75,7 @@ const EditorWrapper = memo(function EditorWrapper({
className={clsx(editor, {
'full-screen': appSettings?.fullWidthLayout,
})}
key={`${workspace.flavour}-${workspace.id}-${pageId}`}
key={`${workspace.id}-${pageId}`}
mode={isPublic ? 'page' : currentMode}
page={page}
onInit={useCallback(
@@ -181,12 +179,11 @@ const LayoutPanel = memo(function LayoutPanel(
export const PageDetailEditor: FC<PageDetailEditorProps> = props => {
const { workspace, pageId } = props;
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
const page = useBlockSuiteWorkspacePage(blockSuiteWorkspace, pageId);
const page = useBlockSuiteWorkspacePage(workspace, pageId);
if (!page) {
throw new PageNotFoundError(blockSuiteWorkspace, pageId);
throw new PageNotFoundError(workspace, pageId);
}
const title = useBlockSuiteWorkspacePageTitle(blockSuiteWorkspace, pageId);
const title = useBlockSuiteWorkspacePageTitle(workspace, pageId);
const layout = useAtomValue(contentLayoutAtom);
const affinePluginsMap = useAtomValue(affinePluginsAtom);
@@ -41,7 +41,7 @@ export const HelpIsland = ({
setOpenSettingModalAtom({
open: true,
activeTab: 'about',
workspace: null,
workspaceId: null,
});
}, [setOpenSettingModalAtom]);
@@ -1,9 +1,5 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import {
DeleteTemporarilyIcon,
FolderIcon,
SettingsIcon,
} from '@blocksuite/icons';
import { DeleteTemporarilyIcon, FolderIcon } from '@blocksuite/icons';
import type { FC, SVGProps } from 'react';
import { useMemo } from 'react';
@@ -23,11 +19,11 @@ export const useSwitchToConfig = (
href: pathGenerator.all(workspaceId),
icon: FolderIcon,
},
{
title: t['Workspace Settings'](),
href: pathGenerator.setting(workspaceId),
icon: SettingsIcon,
},
// {
// title: t['Workspace Settings'](),
// href: pathGenerator.setting(workspaceId),
// icon: SettingsIcon,
// },
{
title: t['Trash'](),
href: pathGenerator.trash(workspaceId),
@@ -14,6 +14,7 @@ import type {
} from '@affine/env/workspace';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
import { HelpIcon, ImportIcon, PlusIcon } from '@blocksuite/icons';
import type { DragEndEvent } from '@dnd-kit/core';
import { useCallback, useRef } from 'react';
@@ -40,12 +41,12 @@ import {
interface WorkspaceModalProps {
disabled?: boolean;
workspaces: AllWorkspace[];
workspaces: RootWorkspaceMetadata[];
currentWorkspaceId: AllWorkspace['id'] | null;
open: boolean;
onClose: () => void;
onClickWorkspace: (workspace: AllWorkspace) => void;
onClickWorkspaceSetting: (workspace: AllWorkspace) => void;
onClickWorkspace: (workspace: RootWorkspaceMetadata['id']) => void;
onClickWorkspaceSetting: (workspace: RootWorkspaceMetadata['id']) => void;
onNewWorkspace: () => void;
onAddWorkspace: () => void;
onMoveWorkspace: (activeId: string, overId: string) => void;
@@ -57,7 +57,7 @@ export const WorkspaceSelector: React.FC<WorkspaceSelectorProps> = ({
data-testid="workspace-avatar"
className={workspaceAvatarStyle}
size={40}
workspace={currentWorkspace}
workspace={currentWorkspace?.blockSuiteWorkspace ?? null}
/>
<StyledSelectorWrapper>
<StyledWorkspaceName data-testid="workspace-name">
@@ -44,7 +44,6 @@ export type RootAppSidebarProps = {
paths: {
all: (workspaceId: string) => string;
trash: (workspaceId: string) => string;
setting: (workspaceId: string) => string;
shared: (workspaceId: string) => string;
};
};
@@ -173,16 +172,6 @@ export const RootAppSidebar = ({
>
<span data-testid="all-pages">{t['All pages']()}</span>
</RouteMenuLinkItem>
{!runtimeConfig.enableNewSettingModal && (
<RouteMenuLinkItem
data-testid="slider-bar-workspace-setting-button"
icon={<SettingsIcon />}
currentPath={currentPath}
path={currentWorkspaceId && paths.setting(currentWorkspaceId)}
>
<span data-testid="settings">{t['Settings']()}</span>
</RouteMenuLinkItem>
)}
{runtimeConfig.enableNewSettingModal ? (
<MenuItem
data-testid="slider-bar-workspace-setting-button"
+5 -1
View File
@@ -14,12 +14,13 @@ import type { ReactElement } from 'react';
import { useCallback } from 'react';
import { useGetPageInfoById } from '../hooks/use-get-page-info';
import { useWorkspace } from '../hooks/use-workspace';
import { BlockSuiteEditorHeader } from './blocksuite/workspace-header';
import { filterContainerStyle } from './filter-container.css';
import { WorkspaceModeFilterTab, WorkspaceTitle } from './pure/workspace-title';
export function WorkspaceHeader({
currentWorkspace,
currentWorkspaceId,
currentEntry,
}: WorkspaceHeaderProps<WorkspaceFlavour>): ReactElement {
const setting = useCollectionManager();
@@ -31,6 +32,9 @@ export function WorkspaceHeader({
},
[setting]
);
const currentWorkspace = useWorkspace(currentWorkspaceId);
const getPageInfoById = useGetPageInfoById();
if ('subPath' in currentEntry) {
if (currentEntry.subPath === WorkspaceSubPath.ALL) {