mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 17:46:18 +08:00
fix: add router defend
This commit is contained in:
@@ -1,20 +1,3 @@
|
||||
import {
|
||||
StyledLoadingWrapper,
|
||||
StyledLoading,
|
||||
StyledLoadingItem,
|
||||
} from './styled';
|
||||
|
||||
export const Loading = ({ size = 40 }: { size?: number }) => {
|
||||
return (
|
||||
<StyledLoadingWrapper size={size}>
|
||||
<StyledLoading>
|
||||
<StyledLoadingItem size={size} />
|
||||
<StyledLoadingItem size={size} />
|
||||
<StyledLoadingItem size={size} />
|
||||
<StyledLoadingItem size={size} />
|
||||
</StyledLoading>
|
||||
</StyledLoadingWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
import Loading from './loading';
|
||||
export * from './page-loading';
|
||||
export default Loading;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
StyledLoadingWrapper,
|
||||
StyledLoading,
|
||||
StyledLoadingItem,
|
||||
} from './styled';
|
||||
|
||||
export const Loading = ({ size = 40 }: { size?: number }) => {
|
||||
return (
|
||||
<StyledLoadingWrapper size={size}>
|
||||
<StyledLoading>
|
||||
<StyledLoadingItem size={size} />
|
||||
<StyledLoadingItem size={size} />
|
||||
<StyledLoadingItem size={size} />
|
||||
<StyledLoadingItem size={size} />
|
||||
</StyledLoading>
|
||||
</StyledLoadingWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Loading;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { styled } from '@/styles';
|
||||
import Loading from './loading';
|
||||
|
||||
// Used for the full page loading
|
||||
const StyledLoadingContainer = styled('div')(({ theme }) => {
|
||||
return {
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
color: '#6880FF',
|
||||
h1: {
|
||||
fontSize: '2em',
|
||||
marginTop: '15px',
|
||||
fontWeight: '600',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const PageLoading = ({ text = 'Loading...' }: { text?: string }) => {
|
||||
return (
|
||||
<StyledLoadingContainer>
|
||||
<div className="wrapper">
|
||||
<Loading />
|
||||
<h1>{text}</h1>
|
||||
</div>
|
||||
</StyledLoadingContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageLoading;
|
||||
@@ -2,10 +2,43 @@ import HelpIsland from '@/components/help-island';
|
||||
import { WorkSpaceSliderBar } from '@/components/workspace-slider-bar';
|
||||
import { useRouter } from 'next/router';
|
||||
import { StyledPage, StyledWrapper } from './styles';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { PropsWithChildren, useEffect, useState } from 'react';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
|
||||
export const WorkspaceDefender = ({ children }: PropsWithChildren) => {
|
||||
const router = useRouter();
|
||||
const [workspaceLoaded, setWorkspaceLoaded] = useState(false);
|
||||
const { synced, loadWorkspace, workspacesMeta } = useAppState();
|
||||
|
||||
useEffect(() => {
|
||||
const initWorkspace = async () => {
|
||||
const workspaceId =
|
||||
(router.query.workspaceId as string) ||
|
||||
workspacesMeta?.[0]?.id ||
|
||||
new Date().getTime().toString();
|
||||
|
||||
await loadWorkspace(workspaceId);
|
||||
setWorkspaceLoaded(true);
|
||||
};
|
||||
if (!synced) {
|
||||
return;
|
||||
}
|
||||
|
||||
initWorkspace();
|
||||
}, [
|
||||
loadWorkspace,
|
||||
router.pathname,
|
||||
router.query.workspaceId,
|
||||
synced,
|
||||
workspaceLoaded,
|
||||
workspacesMeta,
|
||||
]);
|
||||
return <>{workspaceLoaded ? children : null}</>;
|
||||
};
|
||||
|
||||
export const WorkspaceLayout = ({ children }: PropsWithChildren) => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<StyledPage>
|
||||
<WorkSpaceSliderBar />
|
||||
@@ -17,4 +50,11 @@ export const WorkspaceLayout = ({ children }: PropsWithChildren) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkspaceLayout;
|
||||
export const Layout = ({ children }: PropsWithChildren) => {
|
||||
return (
|
||||
<WorkspaceDefender>
|
||||
<WorkspaceLayout>{children}</WorkspaceLayout>
|
||||
</WorkspaceDefender>
|
||||
);
|
||||
};
|
||||
export default Layout;
|
||||
|
||||
@@ -140,7 +140,7 @@ export const WorkSpaceSliderBar = () => {
|
||||
</Link>
|
||||
<StyledNewPageButton
|
||||
onClick={async () => {
|
||||
const pageId = await createPage?.current?.();
|
||||
const pageId = await createPage?.();
|
||||
if (pageId) {
|
||||
openPage(pageId);
|
||||
}
|
||||
|
||||
@@ -8,12 +8,15 @@ import '@fontsource/space-mono';
|
||||
import '@fontsource/poppins';
|
||||
import '../utils/print-build-info';
|
||||
import ProviderComposer from '@/components/provider-composer';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import type { PropsWithChildren, ReactElement, ReactNode } from 'react';
|
||||
import type { NextPage } from 'next';
|
||||
import { AppStateProvider } from '@/providers/app-state-provider/provider';
|
||||
import ConfirmProvider from '@/providers/confirm-provider';
|
||||
import { ModalProvider } from '@/providers/global-modal-provider';
|
||||
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { PageLoading } from '@/components/loading';
|
||||
const ThemeProvider = dynamic(() => import('@/providers/themeProvider'), {
|
||||
ssr: false,
|
||||
});
|
||||
@@ -26,7 +29,7 @@ type AppPropsWithLayout = AppProps & {
|
||||
Component: NextPageWithLayout;
|
||||
};
|
||||
|
||||
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
const App = ({ Component, pageProps }: AppPropsWithLayout) => {
|
||||
const getLayout = Component.getLayout || (page => page);
|
||||
|
||||
return (
|
||||
@@ -40,10 +43,24 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
<ConfirmProvider key="ConfirmProvider" />,
|
||||
]}
|
||||
>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
<AppDefender>{getLayout(<Component {...pageProps} />)}</AppDefender>
|
||||
</ProviderComposer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default MyApp;
|
||||
const AppDefender = ({ children }: PropsWithChildren) => {
|
||||
const router = useRouter();
|
||||
|
||||
const { synced } = useAppState();
|
||||
|
||||
useEffect(() => {
|
||||
if (router.pathname === '/') {
|
||||
router.replace('/workspace');
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
return <div>{synced ? children : <PageLoading />}</div>;
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { useRef, useEffect, ReactElement } from 'react';
|
||||
import {
|
||||
useRef,
|
||||
useEffect,
|
||||
useState,
|
||||
ReactElement,
|
||||
PropsWithChildren,
|
||||
} from 'react';
|
||||
import { styled } from '@/styles';
|
||||
import { EditorHeader } from '@/components/header';
|
||||
import EdgelessToolbar from '@/components/edgeless-toolbar';
|
||||
import MobileModal from '@/components/mobile-modal';
|
||||
import {
|
||||
useLoadWorkspace,
|
||||
useLoadPage,
|
||||
} from '@/providers/app-state-provider/hooks';
|
||||
import { useAppState } from '@/providers/app-state-provider/context';
|
||||
import exampleMarkdown from '@/static/example-markdown';
|
||||
import type { NextPageWithLayout } from '../..//_app';
|
||||
import WorkspaceLayout from '@/components/workspace-layout';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
const StyledEditorContainer = styled('div')(({ theme }) => {
|
||||
return {
|
||||
@@ -20,9 +23,8 @@ const StyledEditorContainer = styled('div')(({ theme }) => {
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
const editorContainer = useRef<HTMLDivElement>(null);
|
||||
const workspace = useLoadWorkspace();
|
||||
const page = useLoadPage();
|
||||
const { createEditor, setEditor } = useAppState();
|
||||
const { createEditor, setEditor, currentPage, currentWorkspace } =
|
||||
useAppState();
|
||||
|
||||
useEffect(() => {
|
||||
const ret = () => {
|
||||
@@ -32,29 +34,29 @@ const Page: NextPageWithLayout = () => {
|
||||
}
|
||||
};
|
||||
|
||||
if (!workspace || !page) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const editor = createEditor?.current?.(page);
|
||||
const editor = createEditor?.current?.(currentPage!);
|
||||
if (editor) {
|
||||
editorContainer.current?.appendChild(editor);
|
||||
setEditor?.current?.(editor);
|
||||
if (page.isEmpty) {
|
||||
if (currentPage!.isEmpty) {
|
||||
const title = 'Welcome to the AFFiNE Alpha';
|
||||
const pageId = page.addBlock({
|
||||
const pageId = currentPage!.addBlock({
|
||||
flavour: 'affine:page',
|
||||
title,
|
||||
});
|
||||
const groupId = page.addBlock({ flavour: 'affine:group' }, pageId);
|
||||
const groupId = currentPage!.addBlock(
|
||||
{ flavour: 'affine:group' },
|
||||
pageId
|
||||
);
|
||||
editor.clipboard.importMarkdown(exampleMarkdown, `${groupId}`);
|
||||
workspace.setPageMeta(page.id, { title });
|
||||
page.resetHistory();
|
||||
|
||||
currentWorkspace!.setPageMeta(currentPage!.id, { title });
|
||||
currentPage!.resetHistory();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}, [workspace, page, createEditor, setEditor]);
|
||||
}, [currentWorkspace, currentPage, createEditor, setEditor]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -66,8 +68,34 @@ const Page: NextPageWithLayout = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const PageDefender = ({ children }: PropsWithChildren) => {
|
||||
const router = useRouter();
|
||||
const [pageLoaded, setPageLoaded] = useState(false);
|
||||
const { currentWorkspace, createPage, loadPage } = useAppState();
|
||||
useEffect(() => {
|
||||
const initPage = async () => {
|
||||
const pageId = router.query.pageId as string;
|
||||
const isPageExist = !!currentWorkspace!.meta.pageMetas.find(
|
||||
p => p.id === pageId
|
||||
);
|
||||
if (!isPageExist) {
|
||||
await createPage(pageId);
|
||||
}
|
||||
await loadPage(pageId);
|
||||
setPageLoaded(true);
|
||||
};
|
||||
initPage();
|
||||
}, [createPage, currentWorkspace, loadPage, router.query.pageId]);
|
||||
|
||||
return <>{pageLoaded ? children : null}</>;
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <WorkspaceLayout>{page}</WorkspaceLayout>;
|
||||
return (
|
||||
<WorkspaceLayout>
|
||||
<PageDefender>{page}</PageDefender>
|
||||
</WorkspaceLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { useLoadWorkspace } from '@/providers/app-state-provider/hooks';
|
||||
import { PageList } from '@/components/page-list';
|
||||
import { AllPagesIcon } from '@blocksuite/icons';
|
||||
import usePageMetaList from '@/hooks/use-page-meta-list';
|
||||
import { PageListHeader } from '@/components/header';
|
||||
import { NextPageWithLayout } from '@/pages/_app';
|
||||
import { ReactElement } from 'react';
|
||||
import WorkspaceLayout from '@/components/workspace-layout';
|
||||
|
||||
const All: NextPageWithLayout = () => {
|
||||
const workspace = useLoadWorkspace();
|
||||
const All = () => {
|
||||
const pageMetaList = usePageMetaList();
|
||||
|
||||
return workspace ? (
|
||||
return (
|
||||
<>
|
||||
<PageListHeader icon={<AllPagesIcon />}>All Page</PageListHeader>
|
||||
<PageList
|
||||
@@ -19,7 +16,7 @@ const All: NextPageWithLayout = () => {
|
||||
showFavoriteTag={true}
|
||||
/>
|
||||
</>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
All.getLayout = function getLayout(page: ReactElement) {
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import { useLoadWorkspace } from '@/providers/app-state-provider/hooks';
|
||||
import { PageListHeader } from '@/components/header';
|
||||
import { PageList } from '@/components/page-list';
|
||||
import { FavouritesIcon } from '@blocksuite/icons';
|
||||
import usePageMetaList from '@/hooks/use-page-meta-list';
|
||||
import { NextPageWithLayout } from '@/pages/_app';
|
||||
import { ReactElement } from 'react';
|
||||
import WorkspaceLayout from '@/components/workspace-layout';
|
||||
|
||||
export const Favorite: NextPageWithLayout = () => {
|
||||
const workspace = useLoadWorkspace();
|
||||
export const Favorite = () => {
|
||||
const pageMetaList = usePageMetaList();
|
||||
return workspace ? (
|
||||
return (
|
||||
<>
|
||||
<PageListHeader icon={<FavouritesIcon />}>Favourites</PageListHeader>
|
||||
<PageList pageList={pageMetaList.filter(p => p.favorite && !p.trash)} />
|
||||
</>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
Favorite.getLayout = function getLayout(page: ReactElement) {
|
||||
return <WorkspaceLayout>{page}</WorkspaceLayout>;
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
import {
|
||||
useLoadWorkspace,
|
||||
useLoadPage,
|
||||
} from '@/providers/app-state-provider/hooks';
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useAppState } from '@/providers/app-state-provider/context';
|
||||
import WorkspaceLayout from '@/components/workspace-layout';
|
||||
|
||||
const Page = () => {
|
||||
useLoadWorkspace();
|
||||
useLoadPage();
|
||||
const WorkspaceIndex = () => {
|
||||
const router = useRouter();
|
||||
const { createPage, currentWorkspaceId, currentWorkspace } = useAppState();
|
||||
|
||||
useEffect(() => {
|
||||
const initPage = async () => {
|
||||
const savedPageId = currentWorkspace!.meta.pageMetas[0]?.id;
|
||||
if (savedPageId) {
|
||||
router.replace(`/workspace/${currentWorkspaceId}/${savedPageId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const pageId = await createPage();
|
||||
router.replace(`/workspace/${currentWorkspaceId}/${pageId}`);
|
||||
};
|
||||
initPage();
|
||||
}, [currentWorkspace, currentWorkspaceId, createPage, router]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
WorkspaceIndex.getLayout = function getLayout(page: ReactElement) {
|
||||
return <WorkspaceLayout>{page}</WorkspaceLayout>;
|
||||
};
|
||||
|
||||
export default WorkspaceIndex;
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import { useLoadWorkspace } from '@/providers/app-state-provider/hooks';
|
||||
import { PageListHeader } from '@/components/header';
|
||||
import { PageList } from '@/components/page-list';
|
||||
import { TrashIcon } from '@blocksuite/icons';
|
||||
import usePageMetaList from '@/hooks/use-page-meta-list';
|
||||
import { NextPageWithLayout } from '@/pages/_app';
|
||||
import { ReactElement } from 'react';
|
||||
import WorkspaceLayout from '@/components/workspace-layout';
|
||||
|
||||
export const Trash: NextPageWithLayout = () => {
|
||||
const workspace = useLoadWorkspace();
|
||||
export const Trash = () => {
|
||||
const pageMetaList = usePageMetaList();
|
||||
return workspace ? (
|
||||
return (
|
||||
<>
|
||||
<PageListHeader icon={<TrashIcon />}>Trash</PageListHeader>
|
||||
<PageList pageList={pageMetaList.filter(p => p.trash)} isTrash={true} />
|
||||
</>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
Trash.getLayout = function getLayout(page: ReactElement) {
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
const Page = () => {
|
||||
return <div>Hello, there is [workspace]/index.</div>;
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import WorkspaceLayout from '@/components/workspace-layout';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export const WorkspaceIndex = () => {
|
||||
const router = useRouter();
|
||||
const { currentWorkspaceId } = useAppState();
|
||||
useEffect(() => {
|
||||
if (currentWorkspaceId) {
|
||||
router.replace(`/workspace/${currentWorkspaceId}`);
|
||||
}
|
||||
}, [currentWorkspaceId, router]);
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
WorkspaceIndex.getLayout = function getLayout(page: ReactElement) {
|
||||
return <WorkspaceLayout>{page}</WorkspaceLayout>;
|
||||
};
|
||||
|
||||
export default WorkspaceIndex;
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface AppStateValue {
|
||||
currentPage: StorePage | null;
|
||||
|
||||
editor: EditorContainer | null;
|
||||
synced: boolean;
|
||||
}
|
||||
|
||||
export interface AppStateContext extends AppStateValue {
|
||||
@@ -31,15 +32,9 @@ export interface AppStateContext extends AppStateValue {
|
||||
((page: StorePage) => EditorContainer | null) | undefined
|
||||
>;
|
||||
setEditor?: MutableRefObject<((page: EditorContainer) => void) | undefined>;
|
||||
loadWorkspace?: MutableRefObject<
|
||||
((workspaceId: string) => Promise<StoreWorkspace | null> | null) | undefined
|
||||
>;
|
||||
loadPage?: MutableRefObject<
|
||||
((pageId: string) => Promise<StorePage | null> | null) | undefined
|
||||
>;
|
||||
createPage?: MutableRefObject<
|
||||
((pageId?: string) => Promise<string | null>) | undefined
|
||||
>;
|
||||
loadWorkspace: (workspaceId: string) => Promise<StoreWorkspace | null>;
|
||||
loadPage: (pageId: string) => Promise<StorePage | null>;
|
||||
createPage: (pageId?: string) => Promise<string | null>;
|
||||
}
|
||||
|
||||
export const AppState = createContext<AppStateContext>({
|
||||
@@ -56,9 +51,10 @@ export const AppState = createContext<AppStateContext>({
|
||||
setState: () => {},
|
||||
createEditor: undefined,
|
||||
setEditor: undefined,
|
||||
loadWorkspace: undefined,
|
||||
loadPage: undefined,
|
||||
createPage: undefined,
|
||||
loadWorkspace: () => Promise.resolve(null),
|
||||
loadPage: () => Promise.resolve(null),
|
||||
createPage: () => Promise.resolve(null),
|
||||
synced: false,
|
||||
workspaces: {},
|
||||
});
|
||||
|
||||
|
||||
@@ -89,15 +89,12 @@ const DynamicBlocksuite = ({
|
||||
p => p instanceof IndexedDBDocProvider
|
||||
);
|
||||
if (indexDBProvider) {
|
||||
(indexDBProvider as IndexedDBDocProvider).on('synced', async () => {
|
||||
const updates = await downloadWorkspace({ workspaceId });
|
||||
|
||||
if (updates && updates.byteLength) {
|
||||
Workspace.Y.applyUpdate(workspace.doc, new Uint8Array(updates));
|
||||
// if after update, the space:meta is empty, then we need to get map with doc
|
||||
workspace.doc.getMap('space:meta');
|
||||
}
|
||||
|
||||
(indexDBProvider as IndexedDBDocProvider)?.on('synced', async () => {
|
||||
// const updates = await downloadWorkspace({ workspaceId });
|
||||
// updates &&
|
||||
// Workspace.Y.applyUpdate(workspace.doc, new Uint8Array(updates));
|
||||
// if after update, the space:meta is empty, then we need to get map with doc
|
||||
workspace.doc.getMap('space:meta');
|
||||
resolve(workspace);
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,7 @@ export const useLoadWorkspace = () => {
|
||||
const workspaceId = router.query.workspaceId as string;
|
||||
|
||||
useEffect(() => {
|
||||
loadWorkspace?.current?.(workspaceId);
|
||||
loadWorkspace?.(workspaceId);
|
||||
}, [workspaceId, loadWorkspace]);
|
||||
|
||||
return currentWorkspaceId === workspaceId ? currentWorkspace : null;
|
||||
@@ -29,7 +29,7 @@ export const useLoadPage = () => {
|
||||
}
|
||||
const page = pageId ? workspace?.getPage(pageId) : null;
|
||||
if (page) {
|
||||
loadPage?.current?.(pageId);
|
||||
loadPage?.(pageId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export const useLoadPage = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
createPage?.current?.()?.then(async pageId => {
|
||||
createPage?.()?.then(async pageId => {
|
||||
if (!pageId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
WorkspaceType,
|
||||
getWorkspaceDetail,
|
||||
} from '@pathfinder/data-services';
|
||||
import { AppState } from './context';
|
||||
import { AppState, AppStateContext } from './context';
|
||||
import type {
|
||||
AppStateValue,
|
||||
CreateEditorHandler,
|
||||
@@ -29,6 +29,9 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
|
||||
currentPage: null,
|
||||
editor: null,
|
||||
workspaces: {},
|
||||
// Synced is used to ensure that the provider has synced with the server,
|
||||
// So after Synced set to true, the other state is sure to be set.
|
||||
synced: false,
|
||||
});
|
||||
|
||||
const [loadWorkspaceHandler, _setLoadWorkspaceHandler] =
|
||||
@@ -50,12 +53,14 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
|
||||
[_setCreateEditorHandler]
|
||||
);
|
||||
|
||||
const loadWorkspace =
|
||||
useRef<(workspaceId: string) => Promise<Workspace | null> | null>();
|
||||
const loadWorkspace = useRef<AppStateContext['loadWorkspace']>(() =>
|
||||
Promise.resolve(null)
|
||||
);
|
||||
loadWorkspace.current = async (workspaceId: string) => {
|
||||
if (state.currentWorkspaceId === workspaceId) {
|
||||
return state.currentWorkspace;
|
||||
}
|
||||
|
||||
const workspace = (await loadWorkspaceHandler?.(workspaceId, true)) || null;
|
||||
|
||||
// @ts-expect-error
|
||||
@@ -72,7 +77,9 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
|
||||
}));
|
||||
return workspace;
|
||||
};
|
||||
const loadPage = useRef<(pageId: string) => Promise<Page | null> | null>();
|
||||
const loadPage = useRef<AppStateContext['loadPage']>(() =>
|
||||
Promise.resolve(null)
|
||||
);
|
||||
loadPage.current = async (pageId: string) => {
|
||||
const { currentWorkspace, currentPage } = state;
|
||||
if (pageId === currentPage?.pageId) {
|
||||
@@ -114,7 +121,9 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
|
||||
setState(state => ({ ...state, editor }));
|
||||
};
|
||||
|
||||
const createPage = useRef<(pageId?: string) => Promise<string | null>>();
|
||||
const createPage = useRef<AppStateContext['createPage']>(() =>
|
||||
Promise.resolve(null)
|
||||
);
|
||||
|
||||
createPage.current = (pageId: string = Date.now().toString()) =>
|
||||
new Promise<string | null>(resolve => {
|
||||
@@ -130,8 +139,16 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!loadWorkspaceHandler) {
|
||||
return;
|
||||
}
|
||||
const callback = async (user: AccessTokenMessage | null) => {
|
||||
const workspacesMeta = user ? await getWorkspaces() : [];
|
||||
const workspacesMeta = user
|
||||
? await getWorkspaces().catch(() => {
|
||||
return [];
|
||||
})
|
||||
: [];
|
||||
|
||||
const workspacesList = await Promise.all(
|
||||
workspacesMeta.map(async ({ id }) => {
|
||||
const workspace = (await loadWorkspaceHandler?.(id)) || null;
|
||||
@@ -146,7 +163,13 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
|
||||
});
|
||||
|
||||
// TODO: add meta info to workspace meta
|
||||
setState(state => ({ ...state, user: user, workspacesMeta, workspaces }));
|
||||
setState(state => ({
|
||||
...state,
|
||||
user: user,
|
||||
workspacesMeta,
|
||||
workspaces,
|
||||
synced: true,
|
||||
}));
|
||||
};
|
||||
authorizationEvent.onChange(callback);
|
||||
|
||||
@@ -161,9 +184,9 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
|
||||
setState,
|
||||
createEditor,
|
||||
setEditor,
|
||||
loadWorkspace,
|
||||
loadPage,
|
||||
createPage,
|
||||
loadWorkspace: loadWorkspace.current,
|
||||
loadPage: loadPage.current,
|
||||
createPage: createPage.current,
|
||||
}),
|
||||
[state, setState, loadPage, loadWorkspace]
|
||||
);
|
||||
@@ -174,7 +197,7 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
|
||||
setLoadWorkspaceHandler={setLoadWorkspaceHandler}
|
||||
setCreateEditorHandler={setCreateEditorHandler}
|
||||
/>
|
||||
{loadWorkspaceHandler ? children : null}
|
||||
{children}
|
||||
</AppState.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, useContext, useState } from 'react';
|
||||
import { createContext, useContext, useEffect, useState } from 'react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import ShortcutsModal from '@/components/shortcuts-modal';
|
||||
import ContactModal from '@/components/contact-modal';
|
||||
@@ -49,6 +49,10 @@ export const ModalProvider = ({
|
||||
[key]: visible ?? !modalMap[key],
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
// @ts-ignore
|
||||
window.triggerHandler = () => triggerHandler('login');
|
||||
}, [triggerHandler]);
|
||||
|
||||
return (
|
||||
<ModalContext.Provider
|
||||
|
||||
Reference in New Issue
Block a user