refactor: remove legacy cloud (#2987)

This commit is contained in:
Alex Yang
2023-07-03 22:29:37 +08:00
parent 1887a36ca5
commit dedb4ba833
87 changed files with 148 additions and 6383 deletions

View File

@@ -15,7 +15,6 @@ import type { PropsWithChildren, ReactElement } from 'react';
import React, { lazy, Suspense, useEffect } from 'react';
import { AffineErrorBoundary } from '../components/affine/affine-error-eoundary';
import { MessageCenter } from '../components/pure/message-center';
import type { NextPageWithLayout } from '../shared';
import createEmotionCache from '../utils/create-emotion-cache';
@@ -61,7 +60,6 @@ const App = function App({
return (
<CacheProvider value={emotionCache}>
<I18nextProvider i18n={i18n}>
<MessageCenter />
<AffineErrorBoundary router={useRouter()}>
<AffineContext>
<Head>

View File

@@ -1,105 +0,0 @@
import { Button } from '@affine/component';
import { MainContainer } from '@affine/component/workspace';
import { currentAffineUserAtom } from '@affine/workspace/affine/atom';
import {
clearLoginStorage,
createAffineAuth,
getLoginStorage,
isExpired,
parseIdToken,
setLoginStorage,
SignMethod,
} from '@affine/workspace/affine/login';
import { useAtom } from 'jotai';
import type { NextPage } from 'next';
import { lazy, Suspense, useMemo } from 'react';
import { AppContainer } from '../../components/affine/app-container';
import { toast } from '../../utils';
const Viewer = lazy(() =>
import('@rich-data/viewer').then(m => ({ default: m.JsonViewer }))
);
import { useTheme } from 'next-themes';
const LoginDevPage: NextPage = () => {
const [user, setUser] = useAtom(currentAffineUserAtom);
const auth = useMemo(() => createAffineAuth(), []);
return (
<AppContainer>
<MainContainer>
<h1>LoginDevPage</h1>
<Button
onClick={async () => {
const storage = getLoginStorage();
if (storage) {
const user = parseIdToken(storage.token);
if (isExpired(user)) {
await auth.refreshToken(storage);
}
}
const response = await auth.generateToken(SignMethod.Google);
if (response) {
setLoginStorage(response);
const user = parseIdToken(response.token);
setUser(user);
} else {
toast('Login failed');
}
}}
>
Login
</Button>
<Button
onClick={async () => {
const storage = getLoginStorage();
if (!storage) {
throw new Error('No storage');
}
const response = await auth.refreshToken(storage);
if (response) {
setLoginStorage(response);
const user = parseIdToken(response.token);
setUser(user);
} else {
toast('Login failed');
}
}}
>
Refresh Token
</Button>
<Button
onClick={() => {
clearLoginStorage();
setUser(null);
}}
>
Reset Storage
</Button>
<Button
onClick={async () => {
const status = await fetch('/api/workspace', {
method: 'GET',
headers: {
'Cache-Control': 'no-cache',
Authorization: getLoginStorage()?.token ?? '',
},
}).then(r => r.status);
toast(`Response Status: ${status}`);
}}
>
Check Permission
</Button>
<Suspense>
<Viewer
theme={useTheme().resolvedTheme === 'light' ? 'light' : 'dark'}
value={user}
/>
</Suspense>
</MainContainer>
</AppContainer>
);
};
export default LoginDevPage;

View File

@@ -1,112 +0,0 @@
import { displayFlex, styled } from '@affine/component';
import { Button } from '@affine/component';
import { WorkspaceSubPath } from '@affine/env/workspace';
import type { Permission } from '@affine/env/workspace/legacy-cloud';
import {
SucessfulDuotoneIcon,
UnsucessfulDuotoneIcon,
} from '@blocksuite/icons';
import { NoSsr } from '@mui/material';
import Image from 'next/legacy/image';
import { useRouter } from 'next/router';
import { Suspense } from 'react';
import useSWR from 'swr';
import { QueryKey } from '../../adapters/affine/fetcher';
import { PageLoading } from '../../components/pure/loading';
import { RouteLogic, useRouterHelper } from '../../hooks/use-router-helper';
import type { NextPageWithLayout } from '../../shared';
const InvitePage: NextPageWithLayout = () => {
const router = useRouter();
const { jumpToSubPath } = useRouterHelper(router);
const { data: inviteData } = useSWR<Permission>(
typeof router.query.invite_code === 'string'
? [QueryKey.acceptInvite, router.query.invite_code]
: null
);
if (inviteData?.accepted) {
return (
<StyledContainer>
<Image
src="/imgs/invite-success.svg"
alt=""
layout="fill"
width={300}
height={300}
/>
<Button
type="primary"
shape="round"
onClick={() => {
jumpToSubPath(
inviteData.workspace_id,
WorkspaceSubPath.ALL,
RouteLogic.REPLACE
).catch(err => console.error(err));
}}
>
Go to Workspace
</Button>
<p>
<SucessfulDuotoneIcon />
Successfully joined
</p>
</StyledContainer>
);
}
if (inviteData?.accepted === false) {
return (
<StyledContainer>
<Image src="/imgs/invite-error.svg" alt="" />
<Button
shape="round"
onClick={() => {
router.replace(`/`).catch(err => console.error(err));
}}
>
Back to Home
</Button>
<p>
<UnsucessfulDuotoneIcon />
The link has expired
</p>
</StyledContainer>
);
}
throw new Error('Invalid invite code');
};
export default InvitePage;
InvitePage.getLayout = page => {
return (
<Suspense fallback={<PageLoading />}>
<NoSsr>{page}</NoSsr>
</Suspense>
);
};
const StyledContainer = styled('div')(() => {
return {
height: '100vh',
...displayFlex('center', 'center'),
flexDirection: 'column',
backgroundColor: 'var(--affine-background-primary-color)',
img: {
width: '300px',
height: '300px',
},
p: {
...displayFlex('center', 'center'),
marginTop: '24px',
svg: {
color: 'var(--affine-primary-color)',
fontSize: '24px',
marginRight: '12px',
},
},
};
});

View File

@@ -1,134 +0,0 @@
import { Breadcrumbs, IconButton, ListSkeleton } from '@affine/component';
import { StyledTableContainer } from '@affine/component/page-list';
import { QueryParamError } from '@affine/env/constant';
import { rootCurrentWorkspaceIdAtom } from '@affine/workspace/atom';
import { SearchIcon } from '@blocksuite/icons';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import { useAtomValue, useSetAtom } from 'jotai';
import { useRouter } from 'next/router';
import type React from 'react';
import { lazy, Suspense, useCallback, useEffect } from 'react';
import { openQuickSearchModalAtom } from '../../atoms';
import {
publicWorkspaceAtom,
publicWorkspaceIdAtom,
} from '../../atoms/public-workspace';
import { WorkspaceAvatar } from '../../components/pure/footer';
import { PageLoading } from '../../components/pure/loading';
import {
PublicQuickSearch,
PublicWorkspaceLayout,
} from '../../layouts/public-workspace-layout';
import type { NextPageWithLayout } from '../../shared';
import { NavContainer, StyledBreadcrumbs } from './[workspaceId]/[pageId]';
const BlockSuitePageList = lazy(() =>
import('../../components/blocksuite/block-suite-page-list').then(module => ({
default: module.BlockSuitePageList,
}))
);
const ListPageInner: React.FC<{
workspaceId: string;
}> = ({ workspaceId }) => {
const router = useRouter();
const publicWorkspace = useAtomValue(publicWorkspaceAtom);
const blockSuiteWorkspace = publicWorkspace.blockSuiteWorkspace;
const handleClickPage = useCallback(
(pageId: string) => {
return router.push({
pathname: `/public-workspace/[workspaceId]/[pageId]`,
query: {
workspaceId,
pageId,
},
});
},
[router, workspaceId]
);
const [name] = useBlockSuiteWorkspaceName(blockSuiteWorkspace);
const [avatar] = useBlockSuiteWorkspaceAvatarUrl(blockSuiteWorkspace);
const setSearchModalOpen = useSetAtom(openQuickSearchModalAtom);
const handleOpen = useCallback(() => {
setSearchModalOpen(true);
}, [setSearchModalOpen]);
if (!blockSuiteWorkspace) {
return <PageLoading />;
}
return (
<>
<PublicQuickSearch workspace={publicWorkspace} />
<NavContainer sx={{ px: '20px' }}>
<Breadcrumbs>
<StyledBreadcrumbs
href={`/public-workspace/${blockSuiteWorkspace.id}`}
>
<WorkspaceAvatar size={24} name={name} avatar={avatar} />
<span>{name}</span>
</StyledBreadcrumbs>
</Breadcrumbs>
<IconButton onClick={handleOpen}>
<SearchIcon />
</IconButton>
</NavContainer>
<Suspense
fallback={
<StyledTableContainer>
<ListSkeleton />
</StyledTableContainer>
}
>
<BlockSuitePageList
listType="public"
isPublic={true}
onOpenPage={handleClickPage}
blockSuiteWorkspace={blockSuiteWorkspace}
/>
</Suspense>
</>
);
};
// This is affine only page, so we don't need to dynamic use WorkspacePlugin
const ListPage: NextPageWithLayout = () => {
const router = useRouter();
const workspaceId = router.query.workspaceId;
const setWorkspaceId = useSetAtom(publicWorkspaceIdAtom);
// todo: remove this atom usage here
const setCurrentWorkspaceId = useSetAtom(rootCurrentWorkspaceIdAtom);
useEffect(() => {
if (!router.isReady) {
return;
}
if (typeof workspaceId === 'string') {
setWorkspaceId(workspaceId);
setCurrentWorkspaceId(workspaceId);
}
}, [router.isReady, setCurrentWorkspaceId, setWorkspaceId, workspaceId]);
const value = useAtomValue(publicWorkspaceIdAtom);
if (!router.isReady || !value) {
return <PageLoading />;
}
if (typeof workspaceId !== 'string') {
throw new QueryParamError('workspaceId', workspaceId);
}
return (
<Suspense
fallback={
<StyledTableContainer>
<ListSkeleton />
</StyledTableContainer>
}
>
<ListPageInner workspaceId={workspaceId} />
</Suspense>
);
};
export default ListPage;
ListPage.getLayout = page => {
return <PublicWorkspaceLayout>{page}</PublicWorkspaceLayout>;
};

View File

@@ -1,158 +0,0 @@
import { Breadcrumbs, displayFlex, styled } from '@affine/component';
import { initEmptyPage } from '@affine/env/blocksuite';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { PageIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store';
import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import { useAtom, useAtomValue } from 'jotai';
import Link from 'next/link';
import { useRouter } from 'next/router';
import type { ReactElement } from 'react';
import { Suspense, useCallback, useEffect } from 'react';
import {
publicPageBlockSuiteAtom,
publicWorkspaceIdAtom,
publicWorkspacePageIdAtom,
} from '../../../atoms/public-workspace';
import { BlockSuiteEditorHeader } from '../../../components/blocksuite/workspace-header';
import {
PageDetailEditor,
type PageDetailEditorProps,
} from '../../../components/page-detail-editor';
import { WorkspaceAvatar } from '../../../components/pure/footer';
import { PageLoading } from '../../../components/pure/loading';
import { useRouterHelper } from '../../../hooks/use-router-helper';
import {
PublicQuickSearch,
PublicWorkspaceLayout,
} from '../../../layouts/public-workspace-layout';
import type { NextPageWithLayout } from '../../../shared';
export const NavContainer = styled('div')(() => {
return {
width: '100vw',
height: '52px',
...displayFlex('space-between', 'center'),
backgroundColor: 'var(--affine-background-primary-color)',
};
});
export const StyledBreadcrumbs = styled(Link)(() => {
return {
flex: 1,
...displayFlex('center', 'center'),
paddingLeft: '12px',
span: {
padding: '0 12px',
fontSize: 'var(--affine-font-base)',
lineHeight: 'var(--affine-line-height)',
},
':hover': { color: 'var(--affine-primary-color)' },
transition: 'all .15s',
':visited': {
':hover': { color: 'var(--affine-primary-color)' },
},
};
});
const PublicWorkspaceDetailPageInner = (): ReactElement => {
const pageId = useAtomValue(publicWorkspacePageIdAtom);
assertExists(pageId, 'pageId is null');
const publicWorkspace = useAtomValue(publicPageBlockSuiteAtom);
const blockSuiteWorkspace = publicWorkspace.blockSuiteWorkspace;
if (!blockSuiteWorkspace) {
throw new Error('cannot find workspace');
}
const router = useRouter();
const { openPage } = useRouterHelper(router);
const t = useAFFiNEI18N();
const [name] = useBlockSuiteWorkspaceName(blockSuiteWorkspace);
const [avatar] = useBlockSuiteWorkspaceAvatarUrl(blockSuiteWorkspace);
const pageTitle = blockSuiteWorkspace.meta.getPageMeta(pageId)?.title;
const onLoad = useCallback<NonNullable<PageDetailEditorProps['onLoad']>>(
(_, editor) => {
const { page } = editor;
page.awarenessStore.setReadonly(page, true);
const dispose = editor.slots.pageLinkClicked.on(({ pageId }) => {
return openPage(blockSuiteWorkspace.id, pageId);
});
return () => {
dispose.dispose();
};
},
[blockSuiteWorkspace.id, openPage]
);
return (
<>
<PublicQuickSearch workspace={publicWorkspace} />
<BlockSuiteEditorHeader
isPublic={true}
workspace={publicWorkspace}
currentPage={blockSuiteWorkspace.getPage(pageId)}
>
<NavContainer>
<Breadcrumbs>
<StyledBreadcrumbs
href={`/public-workspace/${blockSuiteWorkspace.id}`}
>
<WorkspaceAvatar size={24} name={name} avatar={avatar} />
<span>{name}</span>
</StyledBreadcrumbs>
<StyledBreadcrumbs
href={`/public-workspace/${blockSuiteWorkspace.id}/${pageId}`}
>
<PageIcon fontSize={24} />
<span>{pageTitle ? pageTitle : t['Untitled']()}</span>
</StyledBreadcrumbs>
</Breadcrumbs>
</NavContainer>
</BlockSuiteEditorHeader>
<PageDetailEditor
isPublic={true}
pageId={pageId}
workspace={publicWorkspace}
onLoad={onLoad}
onInit={initEmptyPage}
/>
</>
);
};
export const PublicWorkspaceDetailPage: NextPageWithLayout = () => {
const router = useRouter();
const [workspaceId, setWorkspaceId] = useAtom(publicWorkspaceIdAtom);
const [pageId, setPageId] = useAtom(publicWorkspacePageIdAtom);
useEffect(() => {
if (!router.isReady) {
return;
}
if (typeof router.query.workspaceId === 'string') {
setWorkspaceId(router.query.workspaceId);
}
if (typeof router.query.pageId === 'string') {
setPageId(router.query.pageId);
}
}, [
router.isReady,
router.query.pageId,
router.query.workspaceId,
setPageId,
setWorkspaceId,
]);
if (!router.isReady || !workspaceId || !pageId) {
return <PageLoading />;
}
return (
<Suspense fallback={<PageLoading />}>
<PublicWorkspaceDetailPageInner />
</Suspense>
);
};
export default PublicWorkspaceDetailPage;
PublicWorkspaceDetailPage.getLayout = page => {
return <PublicWorkspaceLayout>{page}</PublicWorkspaceLayout>;
};

View File

@@ -1,136 +0,0 @@
import type { SettingPanel } from '@affine/env/workspace';
import {
settingPanel,
settingPanelValues,
WorkspaceSubPath,
} from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/store';
import { useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import Head from 'next/head';
import type { NextRouter } from 'next/router';
import { useRouter } from 'next/router';
import React, { useCallback } from 'react';
import { getUIAdapter } from '../../../adapters/workspace';
import { PageLoading } from '../../../components/pure/loading';
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
import { useOnTransformWorkspace } from '../../../hooks/root/use-on-transform-workspace';
import { useAppHelper } from '../../../hooks/use-workspaces';
import { WorkspaceLayout } from '../../../layouts/workspace-layout';
import type { NextPageWithLayout } from '../../../shared';
const settingPanelAtom = atomWithStorage<SettingPanel>(
'workspaceId',
settingPanel.General
);
function useTabRouterSync(
router: NextRouter,
currentTab: SettingPanel,
setCurrentTab: (tab: SettingPanel) => void
): void {
if (!router.isReady) {
return;
}
const queryCurrentTab =
typeof router.query.currentTab === 'string'
? router.query.currentTab
: null;
if (
(queryCurrentTab !== null &&
settingPanelValues.indexOf(queryCurrentTab as SettingPanel) === -1) ||
settingPanelValues.indexOf(currentTab as SettingPanel) === -1
) {
setCurrentTab(settingPanel.General);
router
.replace({
pathname: router.pathname,
query: {
...router.query,
currentTab: settingPanel.General,
},
})
.catch(console.error);
} else if (queryCurrentTab !== currentTab) {
router
.replace({
pathname: router.pathname,
query: {
...router.query,
currentTab: currentTab,
},
})
.catch(console.error);
}
}
const SettingPage: NextPageWithLayout = () => {
const router = useRouter();
const [currentWorkspace] = useCurrentWorkspace();
const t = useAFFiNEI18N();
const [currentTab, setCurrentTab] = useAtom(settingPanelAtom);
const onChangeTab = useCallback(
(tab: SettingPanel) => {
setCurrentTab(tab as SettingPanel);
router
.push({
pathname: router.pathname,
query: {
...router.query,
currentTab: tab,
},
})
.catch(err => {
console.error(err);
});
},
[router, setCurrentTab]
);
useTabRouterSync(router, currentTab, setCurrentTab);
const helper = useAppHelper();
const onDeleteWorkspace = useCallback(async () => {
assertExists(currentWorkspace);
const workspaceId = currentWorkspace.id;
return helper.deleteWorkspace(workspaceId);
}, [currentWorkspace, helper]);
const onTransformWorkspace = useOnTransformWorkspace();
if (
!router.isReady ||
currentWorkspace === null ||
settingPanelValues.indexOf(currentTab as SettingPanel) === -1
) {
return <PageLoading />;
}
const { SettingsDetail, Header } = getUIAdapter(currentWorkspace.flavour);
return (
<>
<Head>
<title>{t['Settings']()} - AFFiNE</title>
</Head>
<Header
currentWorkspace={currentWorkspace}
currentEntry={{
subPath: WorkspaceSubPath.SETTING,
}}
/>
<SettingsDetail
onTransformWorkspace={onTransformWorkspace}
onDeleteWorkspace={onDeleteWorkspace}
currentWorkspace={currentWorkspace}
currentTab={currentTab as SettingPanel}
onChangeTab={onChangeTab}
/>
</>
);
};
export default SettingPage;
SettingPage.getLayout = page => {
return <WorkspaceLayout>{page}</WorkspaceLayout>;
};