mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-08 04:36:13 +08:00
feat: init mobile entry (#7905)
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
export const Component = () => {
|
||||
return <div>/workspace/:workspaceId/all</div>;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const Component = () => {
|
||||
return <div>/workspace/:workspaceId/collection/:collectionId</div>;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const Component = () => {
|
||||
return <div>/workspace/:workspaceId/collection</div>;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const Component = () => {
|
||||
return <div>/workspace/:workspaceId/:pageId</div>;
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
import { AppFallback } from '@affine/core/components/affine/app-container';
|
||||
import { RouteContainer } from '@affine/core/modules/workbench/view/route-container';
|
||||
import { PageNotFound } from '@affine/core/pages/404';
|
||||
import { MobileWorkbenchRoot } from '@affine/core/pages/workspace/workbench-root';
|
||||
import {
|
||||
useLiveData,
|
||||
useServices,
|
||||
WorkspacesService,
|
||||
} from '@toeverything/infra';
|
||||
import { lazy as reactLazy, useEffect, useMemo, useState } from 'react';
|
||||
import { matchPath, useLocation, useParams } from 'react-router-dom';
|
||||
|
||||
import { viewRoutes } from '../../router';
|
||||
import { WorkspaceLayout } from './layout';
|
||||
|
||||
const warpedRoutes = viewRoutes.map(({ path, lazy }) => {
|
||||
const Component = reactLazy(() =>
|
||||
lazy().then(m => ({
|
||||
default: m.Component as React.ComponentType,
|
||||
}))
|
||||
);
|
||||
const route = {
|
||||
Component,
|
||||
};
|
||||
|
||||
return {
|
||||
path,
|
||||
Component: () => {
|
||||
return <RouteContainer route={route} />;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const Component = () => {
|
||||
const { workspacesService } = useServices({
|
||||
WorkspacesService,
|
||||
});
|
||||
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
|
||||
// todo(pengx17): dedupe the code with core
|
||||
// check if we are in detail doc route, if so, maybe render share page
|
||||
const detailDocRoute = useMemo(() => {
|
||||
const match = matchPath(
|
||||
'/workspace/:workspaceId/:docId',
|
||||
location.pathname
|
||||
);
|
||||
if (
|
||||
match &&
|
||||
match.params.docId &&
|
||||
match.params.workspaceId &&
|
||||
// TODO(eyhn): need a better way to check if it's a docId
|
||||
viewRoutes.find(route => matchPath(route.path, '/' + match.params.docId))
|
||||
?.path === '/:pageId'
|
||||
) {
|
||||
return {
|
||||
docId: match.params.docId,
|
||||
workspaceId: match.params.workspaceId,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, [location.pathname]);
|
||||
|
||||
const [workspaceNotFound, setWorkspaceNotFound] = useState(false);
|
||||
const listLoading = useLiveData(workspacesService.list.isRevalidating$);
|
||||
const workspaces = useLiveData(workspacesService.list.workspaces$);
|
||||
const meta = useMemo(() => {
|
||||
return workspaces.find(({ id }) => id === params.workspaceId);
|
||||
}, [workspaces, params.workspaceId]);
|
||||
|
||||
// if listLoading is false, we can show 404 page, otherwise we should show loading page.
|
||||
useEffect(() => {
|
||||
if (listLoading === false && meta === undefined) {
|
||||
setWorkspaceNotFound(true);
|
||||
}
|
||||
if (meta) {
|
||||
setWorkspaceNotFound(false);
|
||||
}
|
||||
}, [listLoading, meta, workspacesService]);
|
||||
|
||||
// if workspace is not found, we should revalidate in interval
|
||||
useEffect(() => {
|
||||
if (listLoading === false && meta === undefined) {
|
||||
const timer = setInterval(
|
||||
() => workspacesService.list.revalidate(),
|
||||
5000
|
||||
);
|
||||
return () => clearInterval(timer);
|
||||
}
|
||||
return;
|
||||
}, [listLoading, meta, workspaceNotFound, workspacesService]);
|
||||
|
||||
if (workspaceNotFound) {
|
||||
if (
|
||||
detailDocRoute /* */ &&
|
||||
environment.isBrowser /* only browser has share page */
|
||||
) {
|
||||
return <div>TODO: share page</div>;
|
||||
}
|
||||
return <PageNotFound noPermission />;
|
||||
}
|
||||
if (!meta) {
|
||||
return <AppFallback key="workspaceLoading" />;
|
||||
}
|
||||
return (
|
||||
<WorkspaceLayout meta={meta}>
|
||||
<MobileWorkbenchRoot routes={warpedRoutes} />
|
||||
</WorkspaceLayout>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
|
||||
import { AppFallback } from '@affine/core/components/affine/app-container';
|
||||
import { WorkspaceLayoutProviders } from '@affine/core/layouts/workspace-layout';
|
||||
import {
|
||||
AllWorkspaceModals,
|
||||
CurrentWorkspaceModals,
|
||||
} from '@affine/core/providers/modal-provider';
|
||||
import { SWRConfigProvider } from '@affine/core/providers/swr-config-provider';
|
||||
import type { Workspace, WorkspaceMetadata } from '@toeverything/infra';
|
||||
import {
|
||||
FrameworkScope,
|
||||
GlobalContextService,
|
||||
useLiveData,
|
||||
useServices,
|
||||
WorkspacesService,
|
||||
} from '@toeverything/infra';
|
||||
import {
|
||||
type PropsWithChildren,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
export const WorkspaceLayout = ({
|
||||
meta,
|
||||
children,
|
||||
}: PropsWithChildren<{ meta: WorkspaceMetadata }>) => {
|
||||
// todo: reduce code duplication with packages\frontend\core\src\pages\workspace\index.tsx
|
||||
const { workspacesService, globalContextService } = useServices({
|
||||
WorkspacesService,
|
||||
GlobalContextService,
|
||||
});
|
||||
|
||||
const [workspace, setWorkspace] = useState<Workspace | null>(null);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const ref = workspacesService.open({ metadata: meta });
|
||||
setWorkspace(ref.workspace);
|
||||
return () => {
|
||||
ref.dispose();
|
||||
};
|
||||
}, [meta, workspacesService]);
|
||||
|
||||
useEffect(() => {
|
||||
if (workspace) {
|
||||
// for debug purpose
|
||||
window.currentWorkspace = workspace ?? undefined;
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('affine:workspace:change', {
|
||||
detail: {
|
||||
id: workspace.id,
|
||||
},
|
||||
})
|
||||
);
|
||||
localStorage.setItem('last_workspace_id', workspace.id);
|
||||
globalContextService.globalContext.workspaceId.set(workspace.id);
|
||||
return () => {
|
||||
window.currentWorkspace = undefined;
|
||||
globalContextService.globalContext.workspaceId.set(null);
|
||||
};
|
||||
}
|
||||
return;
|
||||
}, [globalContextService, workspace]);
|
||||
|
||||
const isRootDocReady =
|
||||
useLiveData(workspace?.engine.rootDocState$.map(v => v.ready)) ?? false;
|
||||
|
||||
if (!workspace) {
|
||||
return null; // skip this, workspace will be set in layout effect
|
||||
}
|
||||
|
||||
if (!isRootDocReady) {
|
||||
return (
|
||||
<FrameworkScope scope={workspace.scope}>
|
||||
<AppFallback key="workspaceLoading" />
|
||||
<AllWorkspaceModals />
|
||||
</FrameworkScope>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FrameworkScope scope={workspace.scope}>
|
||||
<AffineErrorBoundary height="100vh">
|
||||
<SWRConfigProvider>
|
||||
<AllWorkspaceModals />
|
||||
<CurrentWorkspaceModals />
|
||||
<WorkspaceLayoutProviders>{children}</WorkspaceLayoutProviders>
|
||||
</SWRConfigProvider>
|
||||
</AffineErrorBoundary>
|
||||
</FrameworkScope>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const Component = () => {
|
||||
return <div>/workspace/:workspaceId/tag/:tagId</div>;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const Component = () => {
|
||||
return <div>/workspace/:workspaceId/tag</div>;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export const Component = () => {
|
||||
return <div>/workspace/:workspaceId/trash</div>;
|
||||
};
|
||||
Reference in New Issue
Block a user