fix: logic after delete all workspaces (#2587)

Co-authored-by: JimmFly <yangjinfei001@gmail.com>
This commit is contained in:
Himself65
2023-05-31 12:24:14 +08:00
committed by GitHub
parent 1c7ae04f4f
commit 8dbd354659
11 changed files with 100 additions and 77 deletions

View File

@@ -46,7 +46,11 @@ rootWorkspacesMetadataAtom.onMount = setAtom => {
const id = setTimeout(() => {
setAtom(metadata => {
if (abortController.signal.aborted) return metadata;
if (metadata.length === 0) {
if (
metadata.length === 0 &&
localStorage.getItem('is-first-open') === null
) {
localStorage.setItem('is-first-open', 'false');
const newMetadata = createFirst();
logger.info('create first workspace', newMetadata);
return newMetadata;

View File

@@ -1,7 +1,6 @@
import { Button, toast } from '@affine/component';
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import {
ArrowRightSmallIcon,
DeleteIcon,
@@ -12,7 +11,6 @@ import {
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import clsx from 'clsx';
import { useAtomValue } from 'jotai';
import type React from 'react';
import { useEffect, useState } from 'react';
@@ -88,7 +86,6 @@ export const GeneralPanel: React.FC<PanelProps> = ({
workspace.blockSuiteWorkspace
);
const isLastWorkspace = useAtomValue(rootWorkspacesMetadataAtom).length === 1;
return (
<>
<div data-testid="avatar-row" className={style.row}>
@@ -225,14 +222,6 @@ export const GeneralPanel: React.FC<PanelProps> = ({
<div className={style.settingItemLabelHint}>
{t['Delete Workspace Label Hint']()}
</div>
{isOwner && isLastWorkspace && (
<div
className={style.settingsCannotDelete}
data-testid="warn-cannot-delete-last-workspace"
>
{t['com.affine.workspace.cannot-delete']()}
</div>
)}
</div>
<div className={style.col}></div>
@@ -243,7 +232,6 @@ export const GeneralPanel: React.FC<PanelProps> = ({
type="warning"
data-testid="delete-workspace-button"
size="middle"
disabled={isLastWorkspace}
icon={<DeleteIcon />}
onClick={() => {
setShowDelete(true);

View File

@@ -191,22 +191,20 @@ describe('useWorkspaces', () => {
const { result } = renderHook(() => useAppHelper(), {
wrapper: ProviderWrapper,
});
// next tick
await new Promise(resolve => setTimeout(resolve, 100));
{
const workspaces = await store.get(workspacesAtom);
expect(workspaces.length).toEqual(1);
expect(workspaces.length).toEqual(0);
}
await result.current.createLocalWorkspace('test');
{
const workspaces = await store.get(workspacesAtom);
expect(workspaces.length).toEqual(2);
expect(workspaces.length).toEqual(1);
}
const { result: result2 } = renderHook(() => useWorkspaces(), {
wrapper: ProviderWrapper,
});
expect(result2.current.length).toEqual(2);
const firstWorkspace = result2.current[1];
expect(result2.current.length).toEqual(1);
const firstWorkspace = result2.current[0];
expect(firstWorkspace.flavour).toBe('local');
assert(firstWorkspace.flavour === WorkspaceFlavour.LOCAL);
expect(firstWorkspace.blockSuiteWorkspace.meta.name).toBe('test');

View File

@@ -58,7 +58,10 @@ import { useCurrentWorkspace } from '../hooks/current/use-current-workspace';
import { useRouterHelper } from '../hooks/use-router-helper';
import { useRouterTitle } from '../hooks/use-router-title';
import { useWorkspaces } from '../hooks/use-workspaces';
import { ModalProvider } from '../providers/modal-provider';
import {
AllWorkspaceModals,
CurrentWorkspaceModals,
} from '../providers/modal-provider';
import { pathGenerator, publicPathGenerator } from '../shared';
import { toast } from '../utils';
@@ -178,7 +181,10 @@ export const CurrentWorkspaceContext = ({
return () => {
clearTimeout(id);
};
}, [push, exist]);
}, [push, exist, metadata.length]);
if (metadata.length === 0) {
return <WorkspaceFallback key="no-workspace" />;
}
if (!router.isReady) {
return <WorkspaceFallback key="router-is-loading" />;
}
@@ -266,9 +272,10 @@ export const WorkspaceLayout: FC<PropsWithChildren> =
{/* load all workspaces is costly, do not block the whole UI */}
<Suspense fallback={null}>
<AllWorkspaceContext>
<AllWorkspaceModals />
<CurrentWorkspaceContext>
{/* fixme(himself65): don't re-render whole modals */}
<ModalProvider key={currentWorkspaceId} />
<CurrentWorkspaceModals key={currentWorkspaceId} />
</CurrentWorkspaceContext>
</AllWorkspaceContext>
</Suspense>

View File

@@ -1,3 +1,4 @@
import { WorkspaceFallback } from '@affine/component/workspace';
import { DebugLogger } from '@affine/debug';
import { WorkspaceSubPath } from '@affine/workspace/type';
import type { NextPage } from 'next';
@@ -7,6 +8,8 @@ import { Suspense, useEffect } from 'react';
import { PageLoading } from '../components/pure/loading';
import { RouteLogic, useRouterHelper } from '../hooks/use-router-helper';
import { useAppHelper, useWorkspaces } from '../hooks/use-workspaces';
import { AllWorkspaceContext } from '../layouts/workspace-layout';
import { AllWorkspaceModals } from '../providers/modal-provider';
const logger = new DebugLogger('index-page');
@@ -62,7 +65,13 @@ const IndexPageInner = () => {
}
}, [helper, jumpToPage, jumpToSubPath, router, workspaces]);
return <PageLoading key="IndexPageInfinitePageLoading" />;
return (
<Suspense fallback={<WorkspaceFallback />}>
<AllWorkspaceContext>
<AllWorkspaceModals />
</AllWorkspaceContext>
</Suspense>
);
};
const IndexPage: NextPage = () => {

View File

@@ -1,5 +1,4 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import type { SettingPanel } from '@affine/workspace/type';
import {
settingPanel,
@@ -7,7 +6,7 @@ import {
WorkspaceSubPath,
} from '@affine/workspace/type';
import { assertExists } from '@blocksuite/store';
import { useAtom, useAtomValue } from 'jotai';
import { useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import Head from 'next/head';
import type { NextRouter } from 'next/router';
@@ -21,7 +20,6 @@ import { useOnTransformWorkspace } from '../../../hooks/root/use-on-transform-wo
import { useAppHelper } from '../../../hooks/use-workspaces';
import { WorkspaceLayout } from '../../../layouts/workspace-layout';
import type { NextPageWithLayout } from '../../../shared';
import { toast } from '../../../utils';
const settingPanelAtom = atomWithStorage<SettingPanel>(
'workspaceId',
@@ -77,7 +75,6 @@ function useTabRouterSync(
const SettingPage: NextPageWithLayout = () => {
const router = useRouter();
const workspaceIds = useAtomValue(rootWorkspacesMetadataAtom);
const [currentWorkspace] = useCurrentWorkspace();
const t = useAFFiNEI18N();
const [currentTab, setCurrentTab] = useAtom(settingPanelAtom);
@@ -102,13 +99,8 @@ const SettingPage: NextPageWithLayout = () => {
const onDeleteWorkspace = useCallback(async () => {
assertExists(currentWorkspace);
const workspaceId = currentWorkspace.id;
if (workspaceIds.length === 1 && workspaceId === workspaceIds[0].id) {
toast(t['You cannot delete the last workspace']());
throw new Error('You cannot delete the last workspace');
} else {
return await helper.deleteWorkspace(workspaceId);
}
}, [currentWorkspace, helper, t, workspaceIds]);
return helper.deleteWorkspace(workspaceId);
}, [currentWorkspace, helper]);
const onTransformWorkspace = useOnTransformWorkspace();
if (!router.isReady) {
return <PageLoading />;

View File

@@ -1,14 +1,16 @@
import { getEnvironment } from '@affine/env';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import {
rootCurrentWorkspaceIdAtom,
rootWorkspacesMetadataAtom,
} from '@affine/workspace/atom';
import { WorkspaceSubPath } from '@affine/workspace/type';
import { arrayMove } from '@dnd-kit/sortable';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import { useAtom, useSetAtom } from 'jotai';
import { useRouter } from 'next/router';
import type { ReactElement } from 'react';
import { lazy, Suspense, useCallback, useTransition } from 'react';
import {
currentWorkspaceIdAtom,
openCreateWorkspaceModalAtom,
openDisableCloudAlertModalAtom,
openOnboardingModalAtom,
@@ -17,7 +19,6 @@ import {
import { useAffineLogIn } from '../hooks/affine/use-affine-log-in';
import { useAffineLogOut } from '../hooks/affine/use-affine-log-out';
import { useCurrentUser } from '../hooks/current/use-current-user';
import { useCurrentWorkspace } from '../hooks/current/use-current-workspace';
import { useRouterHelper } from '../hooks/use-router-helper';
import { useWorkspaces } from '../hooks/use-workspaces';
@@ -47,14 +48,7 @@ const OnboardingModal = lazy(() =>
}))
);
export function Modals() {
const [openWorkspacesModal, setOpenWorkspacesModal] = useAtom(
openWorkspacesModalAtom
);
const [openCreateWorkspaceModal, setOpenCreateWorkspaceModal] = useAtom(
openCreateWorkspaceModalAtom
);
export function CurrentWorkspaceModals() {
const [openDisableCloudAlertModal, setOpenDisableCloudAlertModal] = useAtom(
openDisableCloudAlertModalAtom
);
@@ -62,14 +56,6 @@ export function Modals() {
openOnboardingModalAtom
);
const router = useRouter();
const { jumpToSubPath } = useRouterHelper(router);
const user = useCurrentUser();
const workspaces = useWorkspaces();
const setWorkspaces = useSetAtom(rootWorkspacesMetadataAtom);
const currentWorkspaceId = useAtomValue(currentWorkspaceIdAtom);
const [, setCurrentWorkspace] = useCurrentWorkspace();
const [transitioning, transition] = useTransition();
const env = getEnvironment();
const onCloseOnboardingModal = useCallback(() => {
setOpenOnboardingModal(false);
@@ -92,14 +78,39 @@ export function Modals() {
/>
</Suspense>
)}
</>
);
}
export const AllWorkspaceModals = (): ReactElement => {
const [openWorkspacesModal, setOpenWorkspacesModal] = useAtom(
openWorkspacesModalAtom
);
const [isOpenCreateWorkspaceModal, setOpenCreateWorkspaceModal] = useAtom(
openCreateWorkspaceModalAtom
);
const router = useRouter();
const { jumpToSubPath } = useRouterHelper(router);
const user = useCurrentUser();
const workspaces = useWorkspaces();
const setWorkspaces = useSetAtom(rootWorkspacesMetadataAtom);
const [currentWorkspaceId, setCurrentWorkspaceId] = useAtom(
rootCurrentWorkspaceIdAtom
);
const [transitioning, transition] = useTransition();
return (
<>
<Suspense>
<WorkspaceListModal
disabled={transitioning}
user={user}
workspaces={workspaces}
currentWorkspaceId={currentWorkspaceId}
open={openWorkspacesModal || workspaces.length === 0}
open={
(openWorkspacesModal || workspaces.length === 0) &&
isOpenCreateWorkspaceModal === false
}
onClose={useCallback(() => {
setOpenWorkspacesModal(false);
}, [setOpenWorkspacesModal])}
@@ -118,18 +129,18 @@ export function Modals() {
onClickWorkspace={useCallback(
workspace => {
setOpenWorkspacesModal(false);
setCurrentWorkspace(workspace.id);
setCurrentWorkspaceId(workspace.id);
jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
},
[jumpToSubPath, setCurrentWorkspace, setOpenWorkspacesModal]
[jumpToSubPath, setCurrentWorkspaceId, setOpenWorkspacesModal]
)}
onClickWorkspaceSetting={useCallback(
workspace => {
setOpenWorkspacesModal(false);
setCurrentWorkspace(workspace.id);
setCurrentWorkspaceId(workspace.id);
jumpToSubPath(workspace.id, WorkspaceSubPath.SETTING);
},
[jumpToSubPath, setCurrentWorkspace, setOpenWorkspacesModal]
[jumpToSubPath, setCurrentWorkspaceId, setOpenWorkspacesModal]
)}
onClickLogin={useAffineLogIn()}
onClickLogout={useAffineLogOut()}
@@ -143,7 +154,7 @@ export function Modals() {
</Suspense>
<Suspense>
<CreateWorkspaceModal
mode={openCreateWorkspaceModal}
mode={isOpenCreateWorkspaceModal}
onClose={useCallback(() => {
setOpenCreateWorkspaceModal(false);
}, [setOpenCreateWorkspaceModal])}
@@ -151,12 +162,12 @@ export function Modals() {
async id => {
setOpenCreateWorkspaceModal(false);
setOpenWorkspacesModal(false);
setCurrentWorkspace(id);
setCurrentWorkspaceId(id);
return jumpToSubPath(id, WorkspaceSubPath.ALL);
},
[
jumpToSubPath,
setCurrentWorkspace,
setCurrentWorkspaceId,
setOpenCreateWorkspaceModal,
setOpenWorkspacesModal,
]
@@ -165,12 +176,4 @@ export function Modals() {
</Suspense>
</>
);
}
export const ModalProvider = (): ReactElement => {
return (
<>
<Modals />
</>
);
};