mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 18:26:05 +08:00
feat(core): allow switch workspace in loading fallback (#6129)
This commit is contained in:
@@ -7,13 +7,15 @@ import { NotificationCenter } from '@affine/component/notification-center';
|
|||||||
import { createI18n, setUpLanguage } from '@affine/i18n';
|
import { createI18n, setUpLanguage } from '@affine/i18n';
|
||||||
import { CacheProvider } from '@emotion/react';
|
import { CacheProvider } from '@emotion/react';
|
||||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
import { getCurrentStore } from '@toeverything/infra/atom';
|
||||||
import { ServiceCollection } from '@toeverything/infra/di';
|
import {
|
||||||
|
ServiceCollection,
|
||||||
|
ServiceProviderContext,
|
||||||
|
} from '@toeverything/infra/di';
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
import { lazy, memo, Suspense } from 'react';
|
import { lazy, memo, Suspense } from 'react';
|
||||||
import { RouterProvider } from 'react-router-dom';
|
import { RouterProvider } from 'react-router-dom';
|
||||||
|
|
||||||
import { WorkspaceFallback } from './components/workspace';
|
import { WorkspaceFallback } from './components/workspace';
|
||||||
import { GlobalScopeProvider } from './modules/infra-web/global-scope';
|
|
||||||
import { CloudSessionProvider } from './providers/session-provider';
|
import { CloudSessionProvider } from './providers/session-provider';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
import { performanceLogger, performanceRenderLogger } from './shared';
|
import { performanceLogger, performanceRenderLogger } from './shared';
|
||||||
@@ -68,7 +70,7 @@ export const App = memo(function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<GlobalScopeProvider provider={serviceProvider}>
|
<ServiceProviderContext.Provider value={serviceProvider}>
|
||||||
<CacheProvider value={cache}>
|
<CacheProvider value={cache}>
|
||||||
<AffineContext store={getCurrentStore()}>
|
<AffineContext store={getCurrentStore()}>
|
||||||
<CloudSessionProvider>
|
<CloudSessionProvider>
|
||||||
@@ -86,7 +88,7 @@ export const App = memo(function App() {
|
|||||||
</CloudSessionProvider>
|
</CloudSessionProvider>
|
||||||
</AffineContext>
|
</AffineContext>
|
||||||
</CacheProvider>
|
</CacheProvider>
|
||||||
</GlobalScopeProvider>
|
</ServiceProviderContext.Provider>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { Skeleton } from '@affine/component';
|
import { Skeleton } from '@affine/component';
|
||||||
import { ResizePanel } from '@affine/component/resize-panel';
|
import { ResizePanel } from '@affine/component/resize-panel';
|
||||||
|
import { Workspace } from '@toeverything/infra';
|
||||||
|
import { useServiceOptional } from '@toeverything/infra/di';
|
||||||
import { useAtom, useAtomValue } from 'jotai';
|
import { useAtom, useAtomValue } from 'jotai';
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { WorkspaceSelector } from '../workspace-selector';
|
||||||
import { fallbackHeaderStyle, fallbackStyle } from './fallback.css';
|
import { fallbackHeaderStyle, fallbackStyle } from './fallback.css';
|
||||||
import {
|
import {
|
||||||
floatingMaxWidth,
|
floatingMaxWidth,
|
||||||
@@ -113,6 +116,8 @@ export function AppSidebar(props: AppSidebarProps): ReactElement {
|
|||||||
|
|
||||||
export const AppSidebarFallback = (): ReactElement | null => {
|
export const AppSidebarFallback = (): ReactElement | null => {
|
||||||
const width = useAtomValue(appSidebarWidthAtom);
|
const width = useAtomValue(appSidebarWidthAtom);
|
||||||
|
|
||||||
|
const currentWorkspace = useServiceOptional(Workspace);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{ width }}
|
style={{ width }}
|
||||||
@@ -125,8 +130,14 @@ export const AppSidebarFallback = (): ReactElement | null => {
|
|||||||
<div className={navBodyStyle}>
|
<div className={navBodyStyle}>
|
||||||
<div className={fallbackStyle}>
|
<div className={fallbackStyle}>
|
||||||
<div className={fallbackHeaderStyle}>
|
<div className={fallbackHeaderStyle}>
|
||||||
<Skeleton variant="circular" width={40} height={40} />
|
{currentWorkspace ? (
|
||||||
<Skeleton variant="rectangular" width={150} height={40} />
|
<WorkspaceSelector />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Skeleton variant="circular" width={40} height={40} />
|
||||||
|
<Skeleton variant="rectangular" width={150} height={40} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export const StyledSelectorContainer = styled('div')({
|
|||||||
padding: '0 6px',
|
padding: '0 6px',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
|
width: '100%',
|
||||||
color: 'var(--affine-text-primary-color)',
|
color: 'var(--affine-text-primary-color)',
|
||||||
':hover': {
|
':hover': {
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { AnimatedDeleteIcon } from '@affine/component';
|
import { AnimatedDeleteIcon } from '@affine/component';
|
||||||
import { Menu } from '@affine/component/ui/menu';
|
|
||||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||||
import { CollectionService } from '@affine/core/modules/collection';
|
import { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { apis, events } from '@affine/electron-api';
|
import { apis, events } from '@affine/electron-api';
|
||||||
@@ -8,12 +7,11 @@ import { FolderIcon, SettingsIcon } from '@blocksuite/icons';
|
|||||||
import { type Doc } from '@blocksuite/store';
|
import { type Doc } from '@blocksuite/store';
|
||||||
import { useDroppable } from '@dnd-kit/core';
|
import { useDroppable } from '@dnd-kit/core';
|
||||||
import { useLiveData, useService, type Workspace } from '@toeverything/infra';
|
import { useLiveData, useService, type Workspace } from '@toeverything/infra';
|
||||||
import { useAtom, useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import type { HTMLAttributes, ReactElement } from 'react';
|
import type { HTMLAttributes, ReactElement } from 'react';
|
||||||
import { forwardRef, Suspense, useCallback, useEffect } from 'react';
|
import { forwardRef, useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import { openWorkspaceListModalAtom } from '../../atoms';
|
|
||||||
import { useAppSettingHelper } from '../../hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '../../hooks/affine/use-app-setting-helper';
|
||||||
import { useDeleteCollectionInfo } from '../../hooks/affine/use-delete-collection-info';
|
import { useDeleteCollectionInfo } from '../../hooks/affine/use-delete-collection-info';
|
||||||
import { getDropItemId } from '../../hooks/affine/use-sidebar-drag';
|
import { getDropItemId } from '../../hooks/affine/use-sidebar-drag';
|
||||||
@@ -41,8 +39,7 @@ import { CollectionsList } from '../pure/workspace-slider-bar/collections';
|
|||||||
import { AddCollectionButton } from '../pure/workspace-slider-bar/collections/add-collection-button';
|
import { AddCollectionButton } from '../pure/workspace-slider-bar/collections/add-collection-button';
|
||||||
import { AddFavouriteButton } from '../pure/workspace-slider-bar/favorite/add-favourite-button';
|
import { AddFavouriteButton } from '../pure/workspace-slider-bar/favorite/add-favourite-button';
|
||||||
import FavoriteList from '../pure/workspace-slider-bar/favorite/favorite-list';
|
import FavoriteList from '../pure/workspace-slider-bar/favorite/favorite-list';
|
||||||
import { UserWithWorkspaceList } from '../pure/workspace-slider-bar/user-with-workspace-list';
|
import { WorkspaceSelector } from '../workspace-selector';
|
||||||
import { WorkspaceCard } from '../pure/workspace-slider-bar/workspace-card';
|
|
||||||
import ImportPage from './import-page';
|
import ImportPage from './import-page';
|
||||||
import { AppSidebarJournalButton } from './journal-button';
|
import { AppSidebarJournalButton } from './journal-button';
|
||||||
import { UpdaterButton } from './updater-button';
|
import { UpdaterButton } from './updater-button';
|
||||||
@@ -102,9 +99,6 @@ export const RootAppSidebar = ({
|
|||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
const docCollection = currentWorkspace.docCollection;
|
const docCollection = currentWorkspace.docCollection;
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const [openUserWorkspaceList, setOpenUserWorkspaceList] = useAtom(
|
|
||||||
openWorkspaceListModalAtom
|
|
||||||
);
|
|
||||||
const currentPath = useLiveData(useService(Workbench).location).pathname;
|
const currentPath = useLiveData(useService(Workbench).location).pathname;
|
||||||
|
|
||||||
const onClickNewPage = useAsyncCallback(async () => {
|
const onClickNewPage = useAsyncCallback(async () => {
|
||||||
@@ -149,10 +143,6 @@ export const RootAppSidebar = ({
|
|||||||
const trashDroppable = useDroppable({
|
const trashDroppable = useDroppable({
|
||||||
id: dropItemId,
|
id: dropItemId,
|
||||||
});
|
});
|
||||||
const closeUserWorkspaceList = useCallback(() => {
|
|
||||||
setOpenUserWorkspaceList(false);
|
|
||||||
}, [setOpenUserWorkspaceList]);
|
|
||||||
const userInfo = useDeleteCollectionInfo();
|
|
||||||
|
|
||||||
const collection = useService(CollectionService);
|
const collection = useService(CollectionService);
|
||||||
const { node, open } = useEditCollectionName({
|
const { node, open } = useEditCollectionName({
|
||||||
@@ -170,6 +160,7 @@ export const RootAppSidebar = ({
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
}, [docCollection.id, collection, navigateHelper, open]);
|
}, [docCollection.id, collection, navigateHelper, open]);
|
||||||
|
const userInfo = useDeleteCollectionInfo();
|
||||||
|
|
||||||
const allPageActive = currentPath === '/all';
|
const allPageActive = currentPath === '/all';
|
||||||
|
|
||||||
@@ -188,31 +179,7 @@ export const RootAppSidebar = ({
|
|||||||
titles={deletePageTitles}
|
titles={deletePageTitles}
|
||||||
/>
|
/>
|
||||||
<SidebarContainer>
|
<SidebarContainer>
|
||||||
<Menu
|
<WorkspaceSelector />
|
||||||
rootOptions={{
|
|
||||||
open: openUserWorkspaceList,
|
|
||||||
}}
|
|
||||||
items={
|
|
||||||
<Suspense>
|
|
||||||
<UserWithWorkspaceList onEventEnd={closeUserWorkspaceList} />
|
|
||||||
</Suspense>
|
|
||||||
}
|
|
||||||
contentOptions={{
|
|
||||||
// hide trigger
|
|
||||||
sideOffset: -58,
|
|
||||||
onInteractOutside: closeUserWorkspaceList,
|
|
||||||
onEscapeKeyDown: closeUserWorkspaceList,
|
|
||||||
style: {
|
|
||||||
width: '300px',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<WorkspaceCard
|
|
||||||
onClick={useCallback(() => {
|
|
||||||
setOpenUserWorkspaceList(true);
|
|
||||||
}, [setOpenUserWorkspaceList])}
|
|
||||||
/>
|
|
||||||
</Menu>
|
|
||||||
<QuickSearchInput
|
<QuickSearchInput
|
||||||
data-testid="slider-bar-quick-search-button"
|
data-testid="slider-bar-quick-search-button"
|
||||||
onClick={onOpenQuickSearchModal}
|
onClick={onOpenQuickSearchModal}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { Menu } from '@affine/component';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
|
import { Suspense, useCallback } from 'react';
|
||||||
|
|
||||||
|
import { openWorkspaceListModalAtom } from '../../atoms';
|
||||||
|
import { UserWithWorkspaceList } from '../pure/workspace-slider-bar/user-with-workspace-list';
|
||||||
|
import { WorkspaceCard } from '../pure/workspace-slider-bar/workspace-card';
|
||||||
|
|
||||||
|
export const WorkspaceSelector = () => {
|
||||||
|
const [isUserWorkspaceListOpened, setOpenUserWorkspaceList] = useAtom(
|
||||||
|
openWorkspaceListModalAtom
|
||||||
|
);
|
||||||
|
const closeUserWorkspaceList = useCallback(() => {
|
||||||
|
setOpenUserWorkspaceList(false);
|
||||||
|
}, [setOpenUserWorkspaceList]);
|
||||||
|
const openUserWorkspaceList = useCallback(() => {
|
||||||
|
setOpenUserWorkspaceList(true);
|
||||||
|
}, [setOpenUserWorkspaceList]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
rootOptions={{
|
||||||
|
open: isUserWorkspaceListOpened,
|
||||||
|
}}
|
||||||
|
items={
|
||||||
|
<Suspense>
|
||||||
|
<UserWithWorkspaceList onEventEnd={closeUserWorkspaceList} />
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
contentOptions={{
|
||||||
|
// hide trigger
|
||||||
|
sideOffset: -58,
|
||||||
|
onInteractOutside: closeUserWorkspaceList,
|
||||||
|
onEscapeKeyDown: closeUserWorkspaceList,
|
||||||
|
style: {
|
||||||
|
width: '300px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<WorkspaceCard onClick={openUserWorkspaceList} />
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,14 +1,7 @@
|
|||||||
import { useWorkspace } from '@affine/core/hooks/use-workspace';
|
import { useWorkspace } from '@affine/core/hooks/use-workspace';
|
||||||
import {
|
import type { Workspace } from '@toeverything/infra';
|
||||||
Workspace,
|
import { WorkspaceListService, WorkspaceManager } from '@toeverything/infra';
|
||||||
WorkspaceListService,
|
import { ServiceProviderContext, useService } from '@toeverything/infra/di';
|
||||||
WorkspaceManager,
|
|
||||||
} from '@toeverything/infra';
|
|
||||||
import {
|
|
||||||
ServiceProviderContext,
|
|
||||||
useService,
|
|
||||||
useServiceOptional,
|
|
||||||
} from '@toeverything/infra/di';
|
|
||||||
import { useLiveData } from '@toeverything/infra/livedata';
|
import { useLiveData } from '@toeverything/infra/livedata';
|
||||||
import { type ReactElement, Suspense, useEffect, useMemo } from 'react';
|
import { type ReactElement, Suspense, useEffect, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
@@ -20,6 +13,10 @@ import { WorkspaceLayout } from '../../layouts/workspace-layout';
|
|||||||
import { RightSidebarContainer } from '../../modules/right-sidebar';
|
import { RightSidebarContainer } from '../../modules/right-sidebar';
|
||||||
import { WorkbenchRoot } from '../../modules/workbench';
|
import { WorkbenchRoot } from '../../modules/workbench';
|
||||||
import { CurrentWorkspaceService } from '../../modules/workspace/current-workspace';
|
import { CurrentWorkspaceService } from '../../modules/workspace/current-workspace';
|
||||||
|
import {
|
||||||
|
AllWorkspaceModals,
|
||||||
|
CurrentWorkspaceModals,
|
||||||
|
} from '../../providers/modal-provider';
|
||||||
import { performanceRenderLogger } from '../../shared';
|
import { performanceRenderLogger } from '../../shared';
|
||||||
import { PageNotFound } from '../404';
|
import { PageNotFound } from '../404';
|
||||||
|
|
||||||
@@ -72,8 +69,6 @@ export const Component = (): ReactElement => {
|
|||||||
localStorage.setItem('last_workspace_id', workspace.id);
|
localStorage.setItem('last_workspace_id', workspace.id);
|
||||||
}, [meta, workspaceManager, workspace, currentWorkspaceService]);
|
}, [meta, workspaceManager, workspace, currentWorkspaceService]);
|
||||||
|
|
||||||
const currentWorkspace = useServiceOptional(Workspace);
|
|
||||||
|
|
||||||
// avoid doing operation, before workspace is loaded
|
// avoid doing operation, before workspace is loaded
|
||||||
const isRootDocLoaded = useLiveData(workspace?.engine.sync.isRootDocLoaded);
|
const isRootDocLoaded = useLiveData(workspace?.engine.sync.isRootDocLoaded);
|
||||||
|
|
||||||
@@ -82,12 +77,22 @@ export const Component = (): ReactElement => {
|
|||||||
return <PageNotFound />;
|
return <PageNotFound />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentWorkspace || !isRootDocLoaded) {
|
if (!workspace) {
|
||||||
return <WorkspaceFallback key="workspaceLoading" />;
|
return <WorkspaceFallback key="workspaceLoading" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isRootDocLoaded) {
|
||||||
|
return (
|
||||||
|
<ServiceProviderContext.Provider value={workspace.services}>
|
||||||
|
<WorkspaceFallback key="workspaceLoading" />
|
||||||
|
<AllWorkspaceModals />
|
||||||
|
<CurrentWorkspaceModals />
|
||||||
|
</ServiceProviderContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ServiceProviderContext.Provider value={currentWorkspace.services}>
|
<ServiceProviderContext.Provider value={workspace.services}>
|
||||||
<Suspense fallback={<WorkspaceFallback key="workspaceFallback" />}>
|
<Suspense fallback={<WorkspaceFallback key="workspaceFallback" />}>
|
||||||
<AffineErrorBoundary height="100vh">
|
<AffineErrorBoundary height="100vh">
|
||||||
<WorkspaceLayout>
|
<WorkspaceLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user