feat: init mobile entry (#7905)

This commit is contained in:
pengx17
2024-08-21 13:17:35 +00:00
parent 3db95bafa2
commit 5acf1b5309
33 changed files with 744 additions and 28 deletions
@@ -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>;
};