mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
feat(mobile): mobile index page UI (#7959)
This commit is contained in:
@@ -1,34 +1,32 @@
|
||||
import type { DocCollection } from '@blocksuite/store';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { Suspense } from 'react';
|
||||
import { type ReactNode, Suspense } from 'react';
|
||||
|
||||
import { useBlockSuitePagePreview } from './use-block-suite-page-preview';
|
||||
import { useDocCollectionPage } from './use-block-suite-workspace-page';
|
||||
|
||||
interface PagePreviewInnerProps {
|
||||
interface PagePreviewProps {
|
||||
docCollection: DocCollection;
|
||||
pageId: string;
|
||||
emptyFallback?: ReactNode;
|
||||
}
|
||||
|
||||
const PagePreviewInner = ({
|
||||
docCollection: workspace,
|
||||
pageId,
|
||||
}: PagePreviewInnerProps) => {
|
||||
emptyFallback,
|
||||
}: PagePreviewProps) => {
|
||||
const page = useDocCollectionPage(workspace, pageId);
|
||||
const previewAtom = useBlockSuitePagePreview(page);
|
||||
const preview = useAtomValue(previewAtom);
|
||||
return preview ? preview : null;
|
||||
const res = preview ? preview : null;
|
||||
return res || emptyFallback;
|
||||
};
|
||||
|
||||
interface PagePreviewProps {
|
||||
docCollection: DocCollection;
|
||||
pageId: string;
|
||||
}
|
||||
|
||||
export const PagePreview = ({ docCollection, pageId }: PagePreviewProps) => {
|
||||
export const PagePreview = (props: PagePreviewProps) => {
|
||||
return (
|
||||
<Suspense>
|
||||
<PagePreviewInner docCollection={docCollection} pageId={pageId} />
|
||||
<PagePreviewInner {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import { map } from 'rxjs';
|
||||
import type { CollapsibleSectionName } from '../types';
|
||||
|
||||
const DEFAULT_COLLAPSABLE_STATE: Record<CollapsibleSectionName, boolean> = {
|
||||
recent: true,
|
||||
favorites: false,
|
||||
organize: false,
|
||||
collections: true,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ExplorerSection } from './entities/explore-section';
|
||||
import { ExplorerService } from './services/explorer';
|
||||
export { ExplorerService } from './services/explorer';
|
||||
export type { CollapsibleSectionName } from './types';
|
||||
export { CollapsibleSection } from './views/layouts/collapsible-section';
|
||||
export { ExplorerMobileContext } from './views/mobile.context';
|
||||
export { ExplorerCollections } from './views/sections/collections';
|
||||
export { ExplorerFavorites } from './views/sections/favorites';
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ExplorerSection } from '../entities/explore-section';
|
||||
import type { CollapsibleSectionName } from '../types';
|
||||
|
||||
const allSectionName: Array<CollapsibleSectionName> = [
|
||||
'recent', // mobile only
|
||||
'favorites',
|
||||
'organize',
|
||||
'collections',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export type CollapsibleSectionName =
|
||||
| 'recent'
|
||||
| 'collections'
|
||||
| 'favorites'
|
||||
| 'tags'
|
||||
|
||||
@@ -77,7 +77,7 @@ export const postfix = style({
|
||||
});
|
||||
export const iconContainer = style({
|
||||
display: 'flex',
|
||||
alignContent: 'center',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: 20,
|
||||
height: 20,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
export { Workbench } from './entities/workbench';
|
||||
export { ViewScope } from './scopes/view';
|
||||
export { WorkbenchService } from './services/workbench';
|
||||
export { useBindWorkbenchToBrowserRouter } from './view/browser-adapter';
|
||||
export { useIsActiveView } from './view/use-is-active-view';
|
||||
export { ViewBody, ViewHeader, ViewSidebarTab } from './view/view-islands';
|
||||
export { ViewIcon, ViewTitle } from './view/view-meta';
|
||||
export type { WorkbenchLinkProps } from './view/workbench-link';
|
||||
export { WorkbenchLink } from './view/workbench-link';
|
||||
export { WorkbenchRoot } from './view/workbench-root';
|
||||
|
||||
|
||||
@@ -10,56 +10,57 @@ import { forwardRef, type MouseEvent } from 'react';
|
||||
|
||||
import { WorkbenchService } from '../services/workbench';
|
||||
|
||||
export const WorkbenchLink = forwardRef<
|
||||
HTMLAnchorElement,
|
||||
React.PropsWithChildren<
|
||||
{
|
||||
to: To;
|
||||
onClick?: (e: MouseEvent) => void;
|
||||
} & React.HTMLProps<HTMLAnchorElement>
|
||||
>
|
||||
>(function WorkbenchLink({ to, onClick, ...other }, ref) {
|
||||
const { featureFlagService, workbenchService } = useServices({
|
||||
FeatureFlagService,
|
||||
WorkbenchService,
|
||||
});
|
||||
const enableMultiView = useLiveData(
|
||||
featureFlagService.flags.enable_multi_view.$
|
||||
);
|
||||
const workbench = workbenchService.workbench;
|
||||
const basename = useLiveData(workbench.basename$);
|
||||
const link =
|
||||
basename +
|
||||
(typeof to === 'string' ? to : `${to.pathname}${to.search}${to.hash}`);
|
||||
const handleClick = useCatchEventCallback(
|
||||
async (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
onClick?.(event);
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
const at = (() => {
|
||||
if (isNewTabTrigger(event)) {
|
||||
return event.altKey && enableMultiView && environment.isDesktop
|
||||
? 'tail'
|
||||
: 'new-tab';
|
||||
}
|
||||
return 'active';
|
||||
})();
|
||||
workbench.open(to, { at });
|
||||
event.preventDefault();
|
||||
},
|
||||
[enableMultiView, onClick, to, workbench]
|
||||
);
|
||||
export type WorkbenchLinkProps = React.PropsWithChildren<
|
||||
{
|
||||
to: To;
|
||||
onClick?: (e: MouseEvent) => void;
|
||||
} & React.HTMLProps<HTMLAnchorElement>
|
||||
>;
|
||||
|
||||
// eslint suspicious runtime error
|
||||
// eslint-disable-next-line react/no-danger-with-children
|
||||
return (
|
||||
<a
|
||||
{...other}
|
||||
ref={ref}
|
||||
href={link}
|
||||
onClick={handleClick}
|
||||
onAuxClick={handleClick}
|
||||
/>
|
||||
);
|
||||
});
|
||||
export const WorkbenchLink = forwardRef<HTMLAnchorElement, WorkbenchLinkProps>(
|
||||
function WorkbenchLink({ to, onClick, ...other }, ref) {
|
||||
const { featureFlagService, workbenchService } = useServices({
|
||||
FeatureFlagService,
|
||||
WorkbenchService,
|
||||
});
|
||||
const enableMultiView = useLiveData(
|
||||
featureFlagService.flags.enable_multi_view.$
|
||||
);
|
||||
const workbench = workbenchService.workbench;
|
||||
const basename = useLiveData(workbench.basename$);
|
||||
const link =
|
||||
basename +
|
||||
(typeof to === 'string' ? to : `${to.pathname}${to.search}${to.hash}`);
|
||||
const handleClick = useCatchEventCallback(
|
||||
async (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
onClick?.(event);
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
const at = (() => {
|
||||
if (isNewTabTrigger(event)) {
|
||||
return event.altKey && enableMultiView && environment.isDesktop
|
||||
? 'tail'
|
||||
: 'new-tab';
|
||||
}
|
||||
return 'active';
|
||||
})();
|
||||
workbench.open(to, { at });
|
||||
event.preventDefault();
|
||||
},
|
||||
[enableMultiView, onClick, to, workbench]
|
||||
);
|
||||
|
||||
// eslint suspicious runtime error
|
||||
// eslint-disable-next-line react/no-danger-with-children
|
||||
return (
|
||||
<a
|
||||
{...other}
|
||||
ref={ref}
|
||||
href={link}
|
||||
onClick={handleClick}
|
||||
onAuxClick={handleClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -28,7 +28,11 @@ export const loader: LoaderFunction = async () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export const Component = () => {
|
||||
export const Component = ({
|
||||
defaultIndexRoute = WorkspaceSubPath.ALL,
|
||||
}: {
|
||||
defaultIndexRoute?: WorkspaceSubPath;
|
||||
}) => {
|
||||
// navigating and creating may be slow, to avoid flickering, we show workspace fallback
|
||||
const [navigating, setNavigating] = useState(true);
|
||||
const [creating, setCreating] = useState(false);
|
||||
@@ -59,11 +63,11 @@ export const Component = () => {
|
||||
if (defaultDocId) {
|
||||
jumpToPage(meta.id, defaultDocId);
|
||||
} else {
|
||||
openPage(meta.id, WorkspaceSubPath.ALL);
|
||||
openPage(meta.id, defaultIndexRoute);
|
||||
}
|
||||
})
|
||||
.catch(err => console.error('Failed to create cloud workspace', err));
|
||||
}, [jumpToPage, openPage, workspacesService]);
|
||||
}, [defaultIndexRoute, jumpToPage, openPage, workspacesService]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!navigating) {
|
||||
@@ -86,7 +90,7 @@ export const Component = () => {
|
||||
const openWorkspace =
|
||||
list.find(w => w.flavour === WorkspaceFlavour.AFFINE_CLOUD) ??
|
||||
list[0];
|
||||
openPage(openWorkspace.id, WorkspaceSubPath.ALL);
|
||||
openPage(openWorkspace.id, defaultIndexRoute);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -99,7 +103,7 @@ export const Component = () => {
|
||||
const lastId = localStorage.getItem('last_workspace_id');
|
||||
|
||||
const openWorkspace = list.find(w => w.id === lastId) ?? list[0];
|
||||
openPage(openWorkspace.id, WorkspaceSubPath.ALL);
|
||||
openPage(openWorkspace.id, defaultIndexRoute);
|
||||
}
|
||||
}, [
|
||||
createCloudWorkspace,
|
||||
@@ -109,6 +113,7 @@ export const Component = () => {
|
||||
listIsLoading,
|
||||
loggedIn,
|
||||
navigating,
|
||||
defaultIndexRoute,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import { useBindWorkbenchToBrowserRouter } from '@affine/core/modules/workbench/view/browser-adapter';
|
||||
import {
|
||||
useBindWorkbenchToBrowserRouter,
|
||||
WorkbenchService,
|
||||
} from '@affine/core/modules/workbench';
|
||||
import { ViewRoot } from '@affine/core/modules/workbench/view/view-root';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
@@ -7,12 +7,14 @@ export enum WorkspaceSubPath {
|
||||
ALL = 'all',
|
||||
TRASH = 'trash',
|
||||
SHARED = 'shared',
|
||||
HOME = 'home',
|
||||
}
|
||||
|
||||
export const WorkspaceSubPathName = {
|
||||
[WorkspaceSubPath.ALL]: 'All Pages',
|
||||
[WorkspaceSubPath.TRASH]: 'Trash',
|
||||
[WorkspaceSubPath.SHARED]: 'Shared',
|
||||
[WorkspaceSubPath.HOME]: 'Home',
|
||||
} satisfies {
|
||||
[Path in WorkspaceSubPath]: string;
|
||||
};
|
||||
@@ -21,6 +23,7 @@ export const pathGenerator = {
|
||||
all: workspaceId => `/workspace/${workspaceId}/all`,
|
||||
trash: workspaceId => `/workspace/${workspaceId}/trash`,
|
||||
shared: workspaceId => `/workspace/${workspaceId}/shared`,
|
||||
home: workspaceId => `/workspace/${workspaceId}/home`,
|
||||
} satisfies {
|
||||
[Path in WorkspaceSubPath]: (workspaceId: string) => string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user