diff --git a/packages/frontend/core/src/atoms/index.ts b/packages/frontend/core/src/atoms/index.ts index cab595dbcb..ce5eb51a39 100644 --- a/packages/frontend/core/src/atoms/index.ts +++ b/packages/frontend/core/src/atoms/index.ts @@ -48,9 +48,6 @@ export const recentPageIdsBaseAtom = atomWithStorage( [] ); -export type PageModeOption = 'all' | 'page' | 'edgeless'; -export const allPageModeSelectAtom = atom('all'); - export type AllPageFilterOption = 'docs' | 'collections' | 'tags'; export const allPageFilterSelectAtom = atom('docs'); diff --git a/packages/frontend/core/src/commands/affine-navigation.tsx b/packages/frontend/core/src/commands/affine-navigation.tsx index 2e2fa58f7c..5fabb25297 100644 --- a/packages/frontend/core/src/commands/affine-navigation.tsx +++ b/packages/frontend/core/src/commands/affine-navigation.tsx @@ -5,11 +5,7 @@ import type { Workspace } from '@blocksuite/store'; import { registerAffineCommand } from '@toeverything/infra/command'; import type { createStore } from 'jotai'; -import { - openSettingModalAtom, - openWorkspaceListModalAtom, - type PageModeOption, -} from '../atoms'; +import { openSettingModalAtom, openWorkspaceListModalAtom } from '../atoms'; import type { useNavigateHelper } from '../hooks/use-navigate-helper'; export function registerAffineNavigationCommands({ @@ -17,12 +13,10 @@ export function registerAffineNavigationCommands({ store, workspace, navigationHelper, - setPageListMode, }: { t: ReturnType; store: ReturnType; navigationHelper: ReturnType; - setPageListMode: React.Dispatch>; workspace: Workspace; }) { const unsubs: Array<() => void> = []; @@ -34,7 +28,6 @@ export function registerAffineNavigationCommands({ label: t['com.affine.cmdk.affine.navigation.goto-all-pages'](), run() { navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL); - setPageListMode('all'); }, }) ); @@ -47,7 +40,6 @@ export function registerAffineNavigationCommands({ label: 'Go to Collection List', run() { navigationHelper.jumpToCollections(workspace.id); - setPageListMode('all'); }, }) ); @@ -60,7 +52,6 @@ export function registerAffineNavigationCommands({ label: 'Go to Tag List', run() { navigationHelper.jumpToTags(workspace.id); - setPageListMode('all'); }, }) ); @@ -101,7 +92,6 @@ export function registerAffineNavigationCommands({ label: t['com.affine.cmdk.affine.navigation.goto-trash'](), run() { navigationHelper.jumpToSubPath(workspace.id, WorkspaceSubPath.TRASH); - setPageListMode('all'); }, }) ); diff --git a/packages/frontend/core/src/components/page-list/__tests__/use-all-page-setting.spec.ts b/packages/frontend/core/src/components/page-list/__tests__/use-all-page-setting.spec.ts deleted file mode 100644 index 809df8a590..0000000000 --- a/packages/frontend/core/src/components/page-list/__tests__/use-all-page-setting.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @vitest-environment happy-dom - */ -import 'fake-indexeddb/auto'; - -import type { CollectionService } from '@affine/core/modules/collection'; -import type { Collection } from '@affine/env/filter'; -import { renderHook } from '@testing-library/react'; -import { LiveData } from '@toeverything/infra'; -import { BehaviorSubject } from 'rxjs'; -import { expect, test } from 'vitest'; - -import { createDefaultFilter, vars } from '../filter/vars'; -import { useCollectionManager } from '../use-collection-manager'; - -const defaultMeta = { tags: { options: [] } }; -const collectionsSubject = new BehaviorSubject([]); - -const mockWorkspaceCollectionService = { - collections: LiveData.from(collectionsSubject, []), - addCollection: (...collections) => { - const prev = collectionsSubject.value; - collectionsSubject.next([...collections, ...prev]); - }, - deleteCollection: (...ids) => { - const prev = collectionsSubject.value; - collectionsSubject.next(prev.filter(v => !ids.includes(v.id))); - }, - updateCollection: (id, updater) => { - const prev = collectionsSubject.value; - collectionsSubject.next( - prev.map(v => { - if (v.id === id) { - return updater(v); - } - return v; - }) - ); - }, -} as CollectionService; - -test('useAllPageSetting', async () => { - const settingHook = renderHook(() => - useCollectionManager(mockWorkspaceCollectionService) - ); - const prevCollection = settingHook.result.current.currentCollection; - expect(settingHook.result.current.savedCollections).toEqual([]); - settingHook.result.current.updateCollection({ - ...settingHook.result.current.currentCollection, - filterList: [createDefaultFilter(vars[0], defaultMeta)], - }); - settingHook.rerender(); - const nextCollection = settingHook.result.current.currentCollection; - expect(nextCollection).not.toBe(prevCollection); - expect(nextCollection.filterList).toEqual([ - createDefaultFilter(vars[0], defaultMeta), - ]); - settingHook.result.current.createCollection({ - ...settingHook.result.current.currentCollection, - id: '1', - }); - settingHook.rerender(); - expect(settingHook.result.current.savedCollections.length).toBe(1); - expect(settingHook.result.current.savedCollections[0].id).toBe('1'); -}); diff --git a/packages/frontend/core/src/components/page-list/collections/virtualized-collection-list.tsx b/packages/frontend/core/src/components/page-list/collections/virtualized-collection-list.tsx index fede1ea01c..fd68998420 100644 --- a/packages/frontend/core/src/components/page-list/collections/virtualized-collection-list.tsx +++ b/packages/frontend/core/src/components/page-list/collections/virtualized-collection-list.tsx @@ -18,19 +18,18 @@ import { CollectionOperationCell } from '../operation-cell'; import { CollectionListItemRenderer } from '../page-group'; import { ListTableHeader } from '../page-header'; import type { CollectionMeta, ItemListHandle, ListItem } from '../types'; -import { useCollectionManager } from '../use-collection-manager'; import type { AllPageListConfig } from '../view'; import { VirtualizedList } from '../virtualized-list'; import { CollectionListHeader } from './collection-list-header'; const useCollectionOperationsRenderer = ({ info, - setting, + service, config, }: { info: DeleteCollectionInfo; config: AllPageListConfig; - setting: ReturnType; + service: CollectionService; }) => { const pageOperationsRenderer = useCallback( (collection: Collection) => { @@ -38,12 +37,12 @@ const useCollectionOperationsRenderer = ({ ); }, - [config, info, setting] + [config, info, service] ); return pageOperationsRenderer; @@ -69,13 +68,13 @@ export const VirtualizedCollectionList = ({ const [selectedCollectionIds, setSelectedCollectionIds] = useState( [] ); - const setting = useCollectionManager(useService(CollectionService)); + const collectionService = useService(CollectionService); const currentWorkspace = useService(Workspace); const info = useDeleteCollectionInfo(); const collectionOperations = useCollectionOperationsRenderer({ info, - setting, + service: collectionService, config, }); @@ -105,8 +104,8 @@ export const VirtualizedCollectionList = ({ }, []); const handleDelete = useCallback(() => { - return setting.deleteCollection(info, ...selectedCollectionIds); - }, [setting, info, selectedCollectionIds]); + return collectionService.deleteCollection(info, ...selectedCollectionIds); + }, [collectionService, info, selectedCollectionIds]); return ( <> diff --git a/packages/frontend/core/src/components/page-list/docs/page-list-header.tsx b/packages/frontend/core/src/components/page-list/docs/page-list-header.tsx index dabcace4ac..4babf7d683 100644 --- a/packages/frontend/core/src/components/page-list/docs/page-list-header.tsx +++ b/packages/frontend/core/src/components/page-list/docs/page-list-header.tsx @@ -10,10 +10,7 @@ import { useCallback, useMemo } from 'react'; import { CollectionService } from '../../../modules/collection'; import { createTagFilter } from '../filter/utils'; -import { - createEmptyCollection, - useCollectionManager, -} from '../use-collection-manager'; +import { createEmptyCollection } from '../use-collection-manager'; import { tagColorMap } from '../utils'; import type { AllPageListConfig } from '../view/edit-collection/edit-collection'; import { @@ -23,38 +20,12 @@ import { import * as styles from './page-list-header.css'; import { PageListNewPageButton } from './page-list-new-page-button'; -export const PageListHeader = ({ workspaceId }: { workspaceId: string }) => { +export const PageListHeader = () => { const t = useAFFiNEI18N(); - const setting = useCollectionManager(useService(CollectionService)); - const { jumpToCollections } = useNavigateHelper(); - - const handleJumpToCollections = useCallback(() => { - jumpToCollections(workspaceId); - }, [jumpToCollections, workspaceId]); const title = useMemo(() => { - if (setting.isDefault) { - return t['com.affine.all-pages.header'](); - } - return ( - <> -
- {t['com.affine.collections.header']()} / -
-
- -
-
- {setting.currentCollection.name} -
- - ); - }, [ - handleJumpToCollections, - setting.currentCollection.name, - setting.isDefault, - t, - ]); + return t['com.affine.all-pages.header'](); + }, [t]); return (
@@ -75,22 +46,19 @@ export const CollectionPageListHeader = ({ workspaceId: string; }) => { const t = useAFFiNEI18N(); - const setting = useCollectionManager(useService(CollectionService)); const { jumpToCollections } = useNavigateHelper(); const handleJumpToCollections = useCallback(() => { jumpToCollections(workspaceId); }, [jumpToCollections, workspaceId]); - const { updateCollection } = useCollectionManager( - useService(CollectionService) - ); + const collectionService = useService(CollectionService); const { node, open } = useEditCollection(config); const handleAddPage = useAsyncCallback(async () => { const ret = await open({ ...collection }, 'page'); - updateCollection(ret); - }, [collection, open, updateCollection]); + collectionService.updateCollection(collection.id, () => ret); + }, [collection, collectionService, open]); return ( <> @@ -103,9 +71,7 @@ export const CollectionPageListHeader = ({
-
- {setting.currentCollection.name} -
+
{collection.name}
- - - ) : null; -}; diff --git a/packages/frontend/core/src/components/page-list/view/collection-list.tsx b/packages/frontend/core/src/components/page-list/view/collection-list.tsx index ec4096df20..fae0183eba 100644 --- a/packages/frontend/core/src/components/page-list/view/collection-list.tsx +++ b/packages/frontend/core/src/components/page-list/view/collection-list.tsx @@ -9,102 +9,73 @@ import type { import type { PropertiesMeta } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { FilterIcon } from '@blocksuite/icons'; -import { useCallback, useState } from 'react'; import { CreateFilterMenu } from '../filter/vars'; -import type { useCollectionManager } from '../use-collection-manager'; import * as styles from './collection-list.css'; import { CollectionOperations } from './collection-operations'; -import { - type AllPageListConfig, - EditCollectionModal, -} from './edit-collection/edit-collection'; +import { type AllPageListConfig } from './edit-collection/edit-collection'; -export const CollectionList = ({ - setting, - propertiesMeta, +export const CollectionPageListOperationsMenu = ({ + collection, allPageListConfig, userInfo, - disable, }: { - setting: ReturnType; - propertiesMeta: PropertiesMeta; + collection: Collection; allPageListConfig: AllPageListConfig; userInfo: DeleteCollectionInfo; - disable?: boolean; }) => { const t = useAFFiNEI18N(); - const [collection, setCollection] = useState(); - const onChange = useCallback( - (filterList: Filter[]) => { - setting.updateCollection({ - ...setting.currentCollection, - filterList, - }); - }, - [setting] - ); - const closeUpdateCollectionModal = useCallback((open: boolean) => { - if (!open) { - setCollection(undefined); - } - }, []); - - const onConfirm = useCallback( - (view: Collection) => { - setting.updateCollection(view); - closeUpdateCollectionModal(false); - }, - [closeUpdateCollectionModal, setting] - ); return ( - {setting.isDefault ? ( - <> - - } - > - - - - - ) : ( - + - - )} + {t['com.affine.filter']()} + + + + ); +}; + +export const AllPageListOperationsMenu = ({ + propertiesMeta, + filterList, + onChangeFilterList, +}: { + propertiesMeta: PropertiesMeta; + filterList: Filter[]; + onChangeFilterList: (filterList: Filter[]) => void; +}) => { + const t = useAFFiNEI18N(); + + return ( + + + } + > + + ); }; diff --git a/packages/frontend/core/src/components/page-list/view/collection-operations.tsx b/packages/frontend/core/src/components/page-list/view/collection-operations.tsx index a4889d6ec5..50e6236c18 100644 --- a/packages/frontend/core/src/components/page-list/view/collection-operations.tsx +++ b/packages/frontend/core/src/components/page-list/view/collection-operations.tsx @@ -7,6 +7,7 @@ import { import type { Collection, DeleteCollectionInfo } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { DeleteIcon, EditIcon, FilterIcon } from '@blocksuite/icons'; +import { useService } from '@toeverything/infra/di'; import { type PropsWithChildren, type ReactElement, @@ -14,7 +15,7 @@ import { useMemo, } from 'react'; -import type { useCollectionManager } from '../use-collection-manager'; +import { CollectionService } from '../../../modules/collection'; import * as styles from './collection-operations.css'; import type { AllPageListConfig } from './index'; import { @@ -25,7 +26,6 @@ import { export const CollectionOperations = ({ collection, config, - setting, info, openRenameModal, children, @@ -33,9 +33,9 @@ export const CollectionOperations = ({ info: DeleteCollectionInfo; collection: Collection; config: AllPageListConfig; - setting: ReturnType; openRenameModal?: () => void; }>) => { + const service = useService(CollectionService); const { open: openEditCollectionModal, node: editModal } = useEditCollection(config); const t = useAFFiNEI18N(); @@ -51,22 +51,25 @@ export const CollectionOperations = ({ } openEditCollectionNameModal(collection.name) .then(name => { - return setting.updateCollection({ ...collection, name }); + return service.updateCollection(collection.id, () => ({ + ...collection, + name, + })); }) .catch(err => { console.error(err); }); - }, [openRenameModal, openEditCollectionNameModal, collection, setting]); + }, [openRenameModal, openEditCollectionNameModal, collection, service]); const showEdit = useCallback(() => { openEditCollectionModal(collection) .then(collection => { - return setting.updateCollection(collection); + return service.updateCollection(collection.id, () => collection); }) .catch(err => { console.error(err); }); - }, [setting, collection, openEditCollectionModal]); + }, [openEditCollectionModal, collection, service]); const actions = useMemo< Array< @@ -112,12 +115,12 @@ export const CollectionOperations = ({ ), name: t['Delete'](), click: () => { - setting.deleteCollection(info, collection.id); + service.deleteCollection(info, collection.id); }, type: 'danger', }, ], - [t, showEditName, showEdit, setting, info, collection.id] + [t, showEditName, showEdit, service, info, collection.id] ); return ( <> diff --git a/packages/frontend/core/src/components/page-list/view/index.ts b/packages/frontend/core/src/components/page-list/view/index.ts index 35f615cd90..396ac12308 100644 --- a/packages/frontend/core/src/components/page-list/view/index.ts +++ b/packages/frontend/core/src/components/page-list/view/index.ts @@ -1,5 +1,4 @@ export * from './affine-shape'; -export * from './collection-bar'; export * from './collection-list'; export * from './collection-operations'; export * from './create-collection'; diff --git a/packages/frontend/core/src/components/page-list/view/use-action.tsx b/packages/frontend/core/src/components/page-list/view/use-action.tsx index 438c92a51b..e524842fab 100644 --- a/packages/frontend/core/src/components/page-list/view/use-action.tsx +++ b/packages/frontend/core/src/components/page-list/view/use-action.tsx @@ -1,10 +1,8 @@ -import type { Collection, DeleteCollectionInfo } from '@affine/env/filter'; +import type { Collection } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { DeleteIcon, FilterIcon } from '@blocksuite/icons'; import { type ReactNode, useMemo } from 'react'; -import type { useCollectionManager } from '../use-collection-manager'; - interface CollectionBarAction { icon: ReactNode; click: () => void; @@ -15,14 +13,12 @@ interface CollectionBarAction { export const useActions = ({ collection, - setting, openEdit, - info, + onDelete, }: { - info: DeleteCollectionInfo; collection: Collection; - setting: ReturnType; openEdit: (open: Collection) => void; + onDelete: () => void; }) => { const t = useAFFiNEI18N(); return useMemo(() => { @@ -39,10 +35,8 @@ export const useActions = ({ icon: , name: 'delete', tooltip: t['com.affine.collection-bar.action.tooltip.delete'](), - click: () => { - setting.deleteCollection(info, collection.id); - }, + click: onDelete, }, ]; - }, [info, collection, t, setting, openEdit]); + }, [t, onDelete, openEdit, collection]); }; diff --git a/packages/frontend/core/src/components/pure/cmdk/data-hooks.tsx b/packages/frontend/core/src/components/pure/cmdk/data-hooks.tsx index 88310a907d..3f37e1c684 100644 --- a/packages/frontend/core/src/components/pure/cmdk/data-hooks.tsx +++ b/packages/frontend/core/src/components/pure/cmdk/data-hooks.tsx @@ -1,4 +1,3 @@ -import { useCollectionManager } from '@affine/core/components/page-list'; import { useBlockSuitePageMeta, usePageMetaHelper, @@ -322,9 +321,8 @@ export const collectionToCommand = ( export const useCollectionsCommands = () => { // todo: considering collections for searching pages - const { savedCollections } = useCollectionManager( - useService(CollectionService) - ); + const collectionService = useService(CollectionService); + const collections = useLiveData(collectionService.collections); const query = useAtomValue(cmdkQueryAtom); const navigationHelper = useNavigateHelper(); const t = useAFFiNEI18N(); @@ -340,7 +338,7 @@ export const useCollectionsCommands = () => { if (query.trim() === '') { return results; } else { - results = savedCollections.map(collection => { + results = collections.map(collection => { const command = collectionToCommand( collection, navigationHelper, @@ -352,14 +350,7 @@ export const useCollectionsCommands = () => { }); return results; } - }, [ - query, - savedCollections, - navigationHelper, - selectCollection, - t, - workspace, - ]); + }, [query, collections, navigationHelper, selectCollection, t, workspace]); }; export const useCMDKCommandGroups = () => { diff --git a/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.tsx b/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.tsx index fa5f22fcd5..6319d4f49a 100644 --- a/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.tsx +++ b/packages/frontend/core/src/components/pure/workspace-mode-filter-tab/index.tsx @@ -4,18 +4,19 @@ import { allPageFilterSelectAtom } from '@affine/core/atoms'; import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; import { WorkspaceSubPath } from '@affine/core/shared'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { useService } from '@toeverything/infra'; +import { Workspace } from '@toeverything/infra'; import { useAtom } from 'jotai'; import { useCallback, useEffect, useState } from 'react'; import * as styles from './index.css'; export const WorkspaceModeFilterTab = ({ - workspaceId, activeFilter, }: { - workspaceId: string; activeFilter: AllPageFilterOption; }) => { + const workspace = useService(Workspace); const t = useAFFiNEI18N(); const [value, setValue] = useState(activeFilter); const [filterMode, setFilterMode] = useAtom(allPageFilterSelectAtom); @@ -24,17 +25,17 @@ export const WorkspaceModeFilterTab = ({ (value: AllPageFilterOption) => { switch (value) { case 'collections': - jumpToCollections(workspaceId); + jumpToCollections(workspace.id); break; case 'tags': - jumpToTags(workspaceId); + jumpToTags(workspace.id); break; case 'docs': - jumpToSubPath(workspaceId, WorkspaceSubPath.ALL); + jumpToSubPath(workspace.id, WorkspaceSubPath.ALL); break; } }, - [jumpToCollections, jumpToSubPath, jumpToTags, workspaceId] + [jumpToCollections, jumpToSubPath, jumpToTags, workspace] ); useEffect(() => { diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx index 2a85c20143..1bff5d5831 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx @@ -6,7 +6,6 @@ import { CollectionOperations, filterPage, stopPropagation, - useCollectionManager, } from '@affine/core/components/page-list'; import { CollectionService } from '@affine/core/modules/collection'; import type { Collection, DeleteCollectionInfo } from '@affine/env/filter'; @@ -40,20 +39,20 @@ const CollectionRenderer = ({ }) => { const [collapsed, setCollapsed] = useState(true); const [open, setOpen] = useState(false); - const setting = useCollectionManager(useService(CollectionService)); + const collectionService = useService(CollectionService); const t = useAFFiNEI18N(); const dragItemId = getDropItemId('collections', collection.id); const removeFromAllowList = useCallback( (id: string) => { - setting.updateCollection({ + collectionService.updateCollection(collection.id, () => ({ ...collection, allowList: collection.allowList?.filter(v => v !== id), - }); + })); toast(t['com.affine.collection.removePage.success']()); }, - [collection, setting, t] + [collection, collectionService, t] ); const { setNodeRef, isOver } = useDroppable({ @@ -66,7 +65,7 @@ const CollectionRenderer = ({ } else { toast(t['com.affine.collection.addPage.success']()); } - setting.addPage(collection.id, id); + collectionService.addPageToCollection(collection.id, id); }, }, }); @@ -95,13 +94,13 @@ const CollectionRenderer = ({ const onRename = useCallback( (name: string) => { - setting.updateCollection({ + collectionService.updateCollection(collection.id, () => ({ ...collection, name, - }); + })); toast(t['com.affine.toastMessage.rename']()); }, - [collection, setting, t] + [collection, collectionService, t] ); const handleOpen = useCallback(() => { setOpen(true); @@ -124,7 +123,6 @@ const CollectionRenderer = ({ diff --git a/packages/frontend/core/src/components/root-app-sidebar/index.tsx b/packages/frontend/core/src/components/root-app-sidebar/index.tsx index 90cb6331fb..137aa88c6f 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx @@ -38,7 +38,6 @@ import { WorkspaceSubPath } from '../../shared'; import { createEmptyCollection, MoveToTrash, - useCollectionManager, useEditCollectionName, } from '../page-list'; import { CollectionsList } from '../pure/workspace-slider-bar/collections'; @@ -177,7 +176,7 @@ export const RootAppSidebar = ({ useRegisterBrowserHistoryCommands(router.back, router.forward); const userInfo = useDeleteCollectionInfo(); - const setting = useCollectionManager(useService(CollectionService)); + const collection = useService(CollectionService); const { node, open } = useEditCollectionName({ title: t['com.affine.editCollection.createCollection'](), showTips: true, @@ -186,13 +185,13 @@ export const RootAppSidebar = ({ open('') .then(name => { const id = nanoid(); - setting.createCollection(createEmptyCollection(id, { name })); + collection.addCollection(createEmptyCollection(id, { name })); navigateHelper.jumpToCollection(blockSuiteWorkspace.id, id); }) .catch(err => { console.error(err); }); - }, [blockSuiteWorkspace.id, navigateHelper, open, setting]); + }, [blockSuiteWorkspace.id, collection, navigateHelper, open]); const allPageActive = useMemo(() => { if ( diff --git a/packages/frontend/core/src/hooks/use-navigate-helper.ts b/packages/frontend/core/src/hooks/use-navigate-helper.ts index 4cc1e772cb..1e3fc10cba 100644 --- a/packages/frontend/core/src/hooks/use-navigate-helper.ts +++ b/packages/frontend/core/src/hooks/use-navigate-helper.ts @@ -44,7 +44,7 @@ export function useNavigateHelper() { ); const jumpToCollections = useCallback( (workspaceId: string, logic: RouteLogic = RouteLogic.PUSH) => { - return navigate(`/workspace/${workspaceId}/all?filterMode=collections`, { + return navigate(`/workspace/${workspaceId}/collection`, { replace: logic === RouteLogic.REPLACE, }); }, @@ -52,7 +52,7 @@ export function useNavigateHelper() { ); const jumpToTags = useCallback( (workspaceId: string, logic: RouteLogic = RouteLogic.PUSH) => { - return navigate(`/workspace/${workspaceId}/all?filterMode=tags`, { + return navigate(`/workspace/${workspaceId}/tag`, { replace: logic === RouteLogic.REPLACE, }); }, diff --git a/packages/frontend/core/src/hooks/use-register-workspace-commands.ts b/packages/frontend/core/src/hooks/use-register-workspace-commands.ts index 3a54a08199..e29252e3f4 100644 --- a/packages/frontend/core/src/hooks/use-register-workspace-commands.ts +++ b/packages/frontend/core/src/hooks/use-register-workspace-commands.ts @@ -1,11 +1,10 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { Workspace } from '@toeverything/infra'; import { useService } from '@toeverything/infra/di'; -import { useSetAtom, useStore } from 'jotai'; +import { useStore } from 'jotai'; import { useTheme } from 'next-themes'; import { useEffect } from 'react'; -import { allPageModeSelectAtom } from '../atoms'; import { registerAffineCreationCommands, registerAffineHelpCommands, @@ -27,7 +26,6 @@ export function useRegisterWorkspaceCommands() { const languageHelper = useLanguageHelper(); const pageHelper = usePageHelper(currentWorkspace.blockSuiteWorkspace); const navigationHelper = useNavigateHelper(); - const setPageListMode = useSetAtom(allPageModeSelectAtom); const [editor] = useActiveBlocksuiteEditor(); // register AffineUpdatesCommands @@ -49,19 +47,12 @@ export function useRegisterWorkspaceCommands() { t, workspace: currentWorkspace.blockSuiteWorkspace, navigationHelper, - setPageListMode, }); return () => { unsub(); }; - }, [ - store, - t, - currentWorkspace.blockSuiteWorkspace, - navigationHelper, - setPageListMode, - ]); + }, [store, t, currentWorkspace.blockSuiteWorkspace, navigationHelper]); // register AffineSettingsCommands useEffect(() => { diff --git a/packages/frontend/core/src/modules/collection/service.ts b/packages/frontend/core/src/modules/collection/service.ts index 69093c7b90..7f69caa2fc 100644 --- a/packages/frontend/core/src/modules/collection/service.ts +++ b/packages/frontend/core/src/modules/collection/service.ts @@ -81,6 +81,15 @@ export class CollectionService { } } + addPageToCollection(collectionId: string, pageId: string) { + this.updateCollection(collectionId, old => { + return { + ...old, + allowList: [pageId, ...(old.allowList ?? [])], + }; + }); + } + deleteCollection(info: DeleteCollectionInfo, ...ids: string[]) { const collectionsYArray = this.collectionsYArray; if (!collectionsYArray) { diff --git a/packages/frontend/core/src/modules/page-list/page-list-view.ts b/packages/frontend/core/src/modules/page-list/page-list-view.ts new file mode 100644 index 0000000000..2638cd3750 --- /dev/null +++ b/packages/frontend/core/src/modules/page-list/page-list-view.ts @@ -0,0 +1,3 @@ +export class PageListView { + constructor() {} +} diff --git a/packages/frontend/core/src/pages/workspace/all-collection/header.tsx b/packages/frontend/core/src/pages/workspace/all-collection/header.tsx new file mode 100644 index 0000000000..76d54670e4 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/all-collection/header.tsx @@ -0,0 +1,49 @@ +import { IconButton } from '@affine/component'; +import { Header } from '@affine/core/components/pure/header'; +import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls'; +import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab'; +import { PlusIcon } from '@blocksuite/icons'; +import clsx from 'clsx'; +import { useMemo } from 'react'; + +import * as styles from '../all-page/all-page.css'; + +export const AllCollectionHeader = ({ + showCreateNew, + onCreateCollection, +}: { + showCreateNew: boolean; + onCreateCollection?: () => void; +}) => { + const isWindowsDesktop = environment.isDesktop && environment.isWindows; + + const renderRightItem = useMemo(() => { + return ( + } + onClick={onCreateCollection} + className={clsx( + styles.headerCreateNewButton, + styles.headerCreateNewCollectionIconButton, + !showCreateNew && styles.headerCreateNewButtonHidden + )} + /> + ); + }, [onCreateCollection, showCreateNew]); + + return ( +
+ {renderRightItem} + {isWindowsDesktop ? : null} + + } + center={} + /> + ); +}; diff --git a/packages/frontend/core/src/pages/workspace/all-collection/index.tsx b/packages/frontend/core/src/pages/workspace/all-collection/index.tsx new file mode 100644 index 0000000000..e95bb6b80a --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/all-collection/index.tsx @@ -0,0 +1,93 @@ +import { HubIsland } from '@affine/core/components/affine/hub-island'; +import { + CollectionListHeader, + type CollectionMeta, + createEmptyCollection, + useEditCollectionName, + VirtualizedCollectionList, +} from '@affine/core/components/page-list'; +import { useAllPageListConfig } from '@affine/core/hooks/affine/use-all-page-list-config'; +import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { useService } from '@toeverything/infra'; +import { useLiveData } from '@toeverything/infra'; +import { Workspace } from '@toeverything/infra'; +import { nanoid } from 'nanoid'; +import { useCallback, useMemo, useState } from 'react'; + +import { CollectionService } from '../../../modules/collection'; +import * as styles from '../all-page/all-page.css'; +import { EmptyCollectionList } from '../page-list-empty'; +import { AllCollectionHeader } from './header'; + +export const AllCollection = () => { + const t = useAFFiNEI18N(); + const currentWorkspace = useService(Workspace); + const [hideHeaderCreateNew, setHideHeaderCreateNew] = useState(true); + + const collectionService = useService(CollectionService); + const collections = useLiveData(collectionService.collections); + const config = useAllPageListConfig(); + + const collectionMetas = useMemo(() => { + const collectionsList: CollectionMeta[] = collections.map(collection => { + return { + ...collection, + title: collection.name, + }; + }); + return collectionsList; + }, [collections]); + + const navigateHelper = useNavigateHelper(); + const { open, node } = useEditCollectionName({ + title: t['com.affine.editCollection.createCollection'](), + showTips: true, + }); + + const handleCreateCollection = useCallback(() => { + open('') + .then(name => { + const id = nanoid(); + collectionService.addCollection(createEmptyCollection(id, { name })); + navigateHelper.jumpToCollection(currentWorkspace.id, id); + }) + .catch(err => { + console.error(err); + }); + }, [collectionService, currentWorkspace, navigateHelper, open]); + + return ( +
+ + {collectionMetas.length > 0 ? ( + + ) : ( + + } + /> + )} + + +
+ ); +}; + +export const Component = () => { + return ; +}; diff --git a/packages/frontend/core/src/pages/workspace/all-page/all-page-filter.tsx b/packages/frontend/core/src/pages/workspace/all-page/all-page-filter.tsx index 94e15aaca1..edee066980 100644 --- a/packages/frontend/core/src/pages/workspace/all-page/all-page-filter.tsx +++ b/packages/frontend/core/src/pages/workspace/all-page/all-page-filter.tsx @@ -8,36 +8,31 @@ import { filterContainerStyle } from '../../../components/filter-container.css'; import { FilterList, SaveAsCollectionButton, - useCollectionManager, } from '../../../components/page-list'; import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; -export const FilterContainer = () => { +export const FilterContainer = ({ + filters, + onChangeFilters, +}: { + filters: Filter[]; + onChangeFilters: (filters: Filter[]) => void; +}) => { const currentWorkspace = useService(Workspace); const navigateHelper = useNavigateHelper(); - const setting = useCollectionManager(useService(CollectionService)); + const collectionService = useService(CollectionService); const saveToCollection = useCallback( (collection: Collection) => { - setting.createCollection({ + collectionService.addCollection({ ...collection, - filterList: setting.currentCollection.filterList, + filterList: filters, }); navigateHelper.jumpToCollection(currentWorkspace.id, collection.id); }, - [setting, navigateHelper, currentWorkspace.id] + [collectionService, filters, navigateHelper, currentWorkspace.id] ); - const onFilterChange = useCallback( - (filterList: Filter[]) => { - setting.updateCollection({ - ...setting.currentCollection, - filterList, - }); - }, - [setting] - ); - - if (!setting.isDefault || !setting.currentCollection.filterList.length) { + if (!filters.length) { return null; } @@ -46,12 +41,12 @@ export const FilterContainer = () => {
- {setting.currentCollection.filterList.length > 0 ? ( + {filters.length > 0 ? ( ) : null}
diff --git a/packages/frontend/core/src/pages/workspace/all-page/all-page-header.tsx b/packages/frontend/core/src/pages/workspace/all-page/all-page-header.tsx index 787a029607..55f6aa19f7 100644 --- a/packages/frontend/core/src/pages/workspace/all-page/all-page-header.tsx +++ b/packages/frontend/core/src/pages/workspace/all-page/all-page-header.tsx @@ -1,69 +1,33 @@ -import { IconButton } from '@affine/component'; -import type { AllPageFilterOption } from '@affine/core/atoms'; import { - CollectionList, + AllPageListOperationsMenu, PageListNewPageButton, - useCollectionManager, } from '@affine/core/components/page-list'; import { Header } from '@affine/core/components/pure/header'; import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls'; import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab'; -import { useAllPageListConfig } from '@affine/core/hooks/affine/use-all-page-list-config'; -import { useDeleteCollectionInfo } from '@affine/core/hooks/affine/use-delete-collection-info'; +import type { Filter } from '@affine/env/filter'; import { PlusIcon } from '@blocksuite/icons'; -import type { Workspace } from '@blocksuite/store'; -import { useService } from '@toeverything/infra/di'; +import { useService } from '@toeverything/infra'; +import { Workspace } from '@toeverything/infra'; import clsx from 'clsx'; import { useMemo } from 'react'; -import { CollectionService } from '../../../modules/collection'; import * as styles from './all-page.css'; import { FilterContainer } from './all-page-filter'; export const AllPageHeader = ({ - workspace, showCreateNew, - isDefaultFilter, - activeFilter, - onCreateCollection, + filters, + onChangeFilters, }: { - workspace: Workspace; showCreateNew: boolean; - isDefaultFilter: boolean; - activeFilter: AllPageFilterOption; - onCreateCollection?: () => void; + filters: Filter[]; + onChangeFilters: (filters: Filter[]) => void; }) => { - const setting = useCollectionManager(useService(CollectionService)); - const config = useAllPageListConfig(); - const userInfo = useDeleteCollectionInfo(); + const workspace = useService(Workspace); const isWindowsDesktop = environment.isDesktop && environment.isWindows; - const disableFilterButton = useMemo(() => { - return activeFilter !== 'docs' && isDefaultFilter; - }, [activeFilter, isDefaultFilter]); - const renderRightItem = useMemo(() => { - if (activeFilter === 'tags') { - return null; - } - if ( - activeFilter === 'collections' && - isDefaultFilter && - onCreateCollection - ) { - return ( - } - onClick={onCreateCollection} - className={clsx( - styles.headerCreateNewButton, - styles.headerCreateNewCollectionIconButton, - !showCreateNew && styles.headerCreateNewButtonHidden - )} - /> - ); - } return ( ); - }, [activeFilter, isDefaultFilter, onCreateCollection, showCreateNew]); + }, [showCreateNew]); return ( <>
} right={ @@ -98,14 +60,9 @@ export const AllPageHeader = ({ {isWindowsDesktop ? : null} } - center={ - - } + center={} /> - + ); }; diff --git a/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx b/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx index 1ca91df95f..9e10b219ec 100644 --- a/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx +++ b/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx @@ -1,211 +1,50 @@ -import type { AllPageFilterOption } from '@affine/core/atoms'; import { HubIsland } from '@affine/core/components/affine/hub-island'; import { - CollectionListHeader, - type CollectionMeta, - createEmptyCollection, - currentCollectionAtom, PageListHeader, - useCollectionManager, - useEditCollectionName, useFilteredPageMetas, - useTagMetas, - VirtualizedCollectionList, VirtualizedPageList, } from '@affine/core/components/page-list'; -import { - TagListHeader, - VirtualizedTagList, -} from '@affine/core/components/page-list/tags'; -import { useAllPageListConfig } from '@affine/core/hooks/affine/use-all-page-list-config'; import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; import { performanceRenderLogger } from '@affine/core/shared'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import type { Filter } from '@affine/env/filter'; import { useService } from '@toeverything/infra'; -import { useLiveData } from '@toeverything/infra'; import { Workspace } from '@toeverything/infra'; -import { useSetAtom } from 'jotai'; -import { nanoid } from 'nanoid'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { useLocation, useParams } from 'react-router-dom'; -import { NIL } from 'uuid'; +import { useEffect, useState } from 'react'; -import { CollectionService } from '../../../modules/collection'; -import { - EmptyCollectionList, - EmptyPageList, - EmptyTagList, -} from '../page-list-empty'; +import { EmptyPageList } from '../page-list-empty'; import * as styles from './all-page.css'; import { AllPageHeader } from './all-page-header'; -// even though it is called all page, it is also being used for collection route as well -export const AllPage = ({ - activeFilter, -}: { - activeFilter: AllPageFilterOption; -}) => { - const t = useAFFiNEI18N(); - const params = useParams(); +export const AllPage = () => { const currentWorkspace = useService(Workspace); const pageMetas = useBlockSuitePageMeta(currentWorkspace.blockSuiteWorkspace); const [hideHeaderCreateNew, setHideHeaderCreateNew] = useState(true); - const collectionService = useService(CollectionService); - const collections = useLiveData(collectionService.collections); - const setting = useCollectionManager(collectionService); - const config = useAllPageListConfig(); - const { tags, tagMetas, filterPageMetaByTag, deleteTags } = useTagMetas( - currentWorkspace.blockSuiteWorkspace, - pageMetas - ); - const filteredPageMetas = useFilteredPageMetas( - 'all', - pageMetas, - currentWorkspace - ); - const tagPageMetas = useMemo(() => { - if (params.tagId) { - return filterPageMetaByTag(params.tagId); - } - return []; - }, [filterPageMetaByTag, params.tagId]); - - const collectionMetas = useMemo(() => { - const collectionsList: CollectionMeta[] = collections.map(collection => { - return { - ...collection, - title: collection.name, - }; - }); - return collectionsList; - }, [collections]); - - const navigateHelper = useNavigateHelper(); - const { open, node } = useEditCollectionName({ - title: t['com.affine.editCollection.createCollection'](), - showTips: true, + const [filters, setFilters] = useState([]); + const filteredPageMetas = useFilteredPageMetas(currentWorkspace, pageMetas, { + filters: filters, }); - const handleCreateCollection = useCallback(() => { - open('') - .then(name => { - const id = nanoid(); - setting.createCollection(createEmptyCollection(id, { name })); - navigateHelper.jumpToCollection(currentWorkspace.id, id); - }) - .catch(err => { - console.error(err); - }); - }, [currentWorkspace.id, navigateHelper, open, setting]); - - const currentTag = useMemo(() => { - if (params.tagId) { - return tags.find(tag => tag.id === params.tagId); - } - return; - }, [params.tagId, tags]); - - const content = useMemo(() => { - if (filteredPageMetas.length > 0 && activeFilter === 'docs') { - return ( - - ); - } else if (activeFilter === 'collections' && !setting.isDefault) { - return ( - - ); - } else if (activeFilter === 'collections' && setting.isDefault) { - return collectionMetas.length > 0 ? ( - - ) : ( - - } - /> - ); - } else if (activeFilter === 'tags') { - if (params.tagId) { - return tagPageMetas.length > 0 ? ( - - ) : ( - } - blockSuiteWorkspace={currentWorkspace.blockSuiteWorkspace} - /> - ); - } - return tags.length > 0 ? ( - - ) : ( - } /> - ); - } - return ( - } - blockSuiteWorkspace={currentWorkspace.blockSuiteWorkspace} - /> - ); - }, [ - activeFilter, - collectionMetas, - collections, - config, - currentTag, - currentWorkspace.blockSuiteWorkspace, - currentWorkspace.id, - deleteTags, - filteredPageMetas.length, - handleCreateCollection, - node, - params.tagId, - setting.currentCollection, - setting.isDefault, - tagMetas, - tagPageMetas, - tags, - ]); - return (
- {content} + {filteredPageMetas.length > 0 ? ( + + ) : ( + } + blockSuiteWorkspace={currentWorkspace.blockSuiteWorkspace} + /> + )}
); @@ -215,21 +54,8 @@ export const Component = () => { performanceRenderLogger.info('AllPage'); const currentWorkspace = useService(Workspace); - const currentCollection = useSetAtom(currentCollectionAtom); const navigateHelper = useNavigateHelper(); - const location = useLocation(); - const activeFilter = useMemo(() => { - const query = new URLSearchParams(location.search); - const filterMode = query.get('filterMode'); - if (filterMode === 'collections') { - return 'collections'; - } else if (filterMode === 'tags') { - return 'tags'; - } - return 'docs'; - }, [location.search]); - useEffect(() => { function checkJumpOnce() { for (const [pageId] of currentWorkspace.blockSuiteWorkspace.pages) { @@ -252,9 +78,5 @@ export const Component = () => { navigateHelper, ]); - useEffect(() => { - currentCollection(NIL); - }, [currentCollection]); - - return ; + return ; }; diff --git a/packages/frontend/core/src/pages/workspace/all-tag/header.tsx b/packages/frontend/core/src/pages/workspace/all-tag/header.tsx new file mode 100644 index 0000000000..48fdc95302 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/all-tag/header.tsx @@ -0,0 +1,23 @@ +import { Header } from '@affine/core/components/pure/header'; +import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls'; +import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab'; + +import * as styles from '../all-page/all-page.css'; + +export const AllTagHeader = () => { + const isWindowsDesktop = environment.isDesktop && environment.isWindows; + + return ( +
+ {isWindowsDesktop ? : null} + + } + center={} + /> + ); +}; diff --git a/packages/frontend/core/src/pages/workspace/all-tag/index.tsx b/packages/frontend/core/src/pages/workspace/all-tag/index.tsx new file mode 100644 index 0000000000..bca4a3f3f3 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/all-tag/index.tsx @@ -0,0 +1,43 @@ +import { HubIsland } from '@affine/core/components/affine/hub-island'; +import { useTagMetas } from '@affine/core/components/page-list'; +import { + TagListHeader, + VirtualizedTagList, +} from '@affine/core/components/page-list/tags'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { useService } from '@toeverything/infra'; +import { Workspace } from '@toeverything/infra'; + +import * as styles from '../all-page/all-page.css'; +import { EmptyTagList } from '../page-list-empty'; +import { AllTagHeader } from './header'; + +export const AllTag = () => { + const currentWorkspace = useService(Workspace); + const pageMetas = useBlockSuitePageMeta(currentWorkspace.blockSuiteWorkspace); + + const { tags, tagMetas, deleteTags } = useTagMetas( + currentWorkspace.blockSuiteWorkspace, + pageMetas + ); + + return ( +
+ + {tags.length > 0 ? ( + + ) : ( + } /> + )} + +
+ ); +}; + +export const Component = () => { + return ; +}; diff --git a/packages/frontend/core/src/pages/workspace/collection.css.ts b/packages/frontend/core/src/pages/workspace/collection/collection.css.ts similarity index 100% rename from packages/frontend/core/src/pages/workspace/collection.css.ts rename to packages/frontend/core/src/pages/workspace/collection/collection.css.ts diff --git a/packages/frontend/core/src/pages/workspace/collection/header.tsx b/packages/frontend/core/src/pages/workspace/collection/header.tsx new file mode 100644 index 0000000000..41b1254f95 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/collection/header.tsx @@ -0,0 +1,49 @@ +import { IconButton } from '@affine/component'; +import { Header } from '@affine/core/components/pure/header'; +import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls'; +import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab'; +import { PlusIcon } from '@blocksuite/icons'; +import clsx from 'clsx'; +import { useMemo } from 'react'; + +import * as styles from '../all-page/all-page.css'; + +export const CollectionDetailHeader = ({ + showCreateNew, + onCreate, +}: { + showCreateNew: boolean; + onCreate: () => void; +}) => { + const isWindowsDesktop = environment.isDesktop && environment.isWindows; + + const renderRightItem = useMemo(() => { + return ( + } + onClick={onCreate} + className={clsx( + styles.headerCreateNewButton, + styles.headerCreateNewCollectionIconButton, + !showCreateNew && styles.headerCreateNewButtonHidden + )} + /> + ); + }, [onCreate, showCreateNew]); + + return ( +
+ {renderRightItem} + {isWindowsDesktop ? : null} + + } + center={} + /> + ); +}; diff --git a/packages/frontend/core/src/pages/workspace/collection.tsx b/packages/frontend/core/src/pages/workspace/collection/index.tsx similarity index 85% rename from packages/frontend/core/src/pages/workspace/collection.tsx rename to packages/frontend/core/src/pages/workspace/collection/index.tsx index 414fbdd44d..379bdda2b5 100644 --- a/packages/frontend/core/src/pages/workspace/collection.tsx +++ b/packages/frontend/core/src/pages/workspace/collection/index.tsx @@ -3,11 +3,11 @@ import { SidebarSwitch, } from '@affine/component/app-sidebar'; import { pushNotificationAtom } from '@affine/component/notification-center'; +import { HubIsland } from '@affine/core/components/affine/hub-island'; import { AffineShapeIcon, - currentCollectionAtom, - useCollectionManager, useEditCollection, + VirtualizedPageList, } from '@affine/core/components/page-list'; import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls'; import { useAllPageListConfig } from '@affine/core/hooks/affine/use-all-page-list-config'; @@ -23,30 +23,54 @@ import { ViewLayersIcon, } from '@blocksuite/icons'; import { Workspace } from '@toeverything/infra'; -import { getCurrentStore } from '@toeverything/infra/atom'; import { useService } from '@toeverything/infra/di'; import { useLiveData } from '@toeverything/infra/livedata'; import { useAtomValue } from 'jotai'; import { useSetAtom } from 'jotai'; import { useCallback, useEffect, useState } from 'react'; -import { type LoaderFunction, redirect, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; -import { useNavigateHelper } from '../../hooks/use-navigate-helper'; -import { WorkspaceSubPath } from '../../shared'; -import { AllPage } from './all-page/all-page'; +import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; +import { WorkspaceSubPath } from '../../../shared'; +import * as allPageStyles from '../all-page/all-page.css'; import * as styles from './collection.css'; +import { CollectionDetailHeader } from './header'; -export const loader: LoaderFunction = async args => { - const rootStore = getCurrentStore(); - if (!args.params.collectionId) { - return redirect('/404'); - } - rootStore.set(currentCollectionAtom, args.params.collectionId); - return null; +export const CollectionDetail = ({ + collection, +}: { + collection: Collection; +}) => { + const config = useAllPageListConfig(); + const { node, open } = useEditCollection(useAllPageListConfig()); + const collectionService = useService(CollectionService); + const [hideHeaderCreateNew, setHideHeaderCreateNew] = useState(true); + + const handleEditCollection = useAsyncCallback(async () => { + const ret = await open({ ...collection }, 'page'); + collectionService.updateCollection(ret.id, () => ret); + }, [collection, collectionService, open]); + + return ( +
+ + + + {node} +
+ ); }; export const Component = function CollectionPage() { const collectionService = useService(CollectionService); + const collections = useLiveData(collectionService.collections); const navigate = useNavigateHelper(); const params = useParams(); @@ -87,7 +111,7 @@ export const Component = function CollectionPage() { return isEmpty(collection) ? ( ) : ( - + ); }; @@ -95,16 +119,16 @@ const isWindowsDesktop = environment.isDesktop && environment.isWindows; const Placeholder = ({ collection }: { collection: Collection }) => { const workspace = useService(Workspace); - const collectionService = useCollectionManager(useService(CollectionService)); + const collectionService = useService(CollectionService); const { node, open } = useEditCollection(useAllPageListConfig()); const { jumpToCollections } = useNavigateHelper(); const openPageEdit = useAsyncCallback(async () => { const ret = await open({ ...collection }, 'page'); - collectionService.updateCollection(ret); + collectionService.updateCollection(ret.id, () => ret); }, [open, collection, collectionService]); const openRuleEdit = useAsyncCallback(async () => { const ret = await open({ ...collection }, 'rule'); - collectionService.updateCollection(ret); + collectionService.updateCollection(ret.id, () => ret); }, [collection, open, collectionService]); const [showTips, setShowTips] = useState(false); useEffect(() => { diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx index b352744e19..e337a3cd9c 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx @@ -2,7 +2,6 @@ import { Scrollable } from '@affine/component'; import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton'; import { ResizePanel } from '@affine/component/resize-panel'; import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; -import { CollectionService } from '@affine/core/modules/collection'; import type { PageService } from '@blocksuite/blocks'; import { BookmarkService, @@ -42,17 +41,13 @@ import { HubIsland } from '../../../components/affine/hub-island'; import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal'; import { ImagePreviewModal } from '../../../components/image-preview'; import { PageDetailEditor } from '../../../components/page-detail-editor'; -import { - createTagFilter, - useCollectionManager, -} from '../../../components/page-list'; import { TrashPageFooter } from '../../../components/pure/trash-page-footer'; import { TopTip } from '../../../components/top-tip'; import { useRegisterBlocksuiteEditorCommands } from '../../../hooks/affine/use-register-blocksuite-editor-commands'; import { usePageDocumentTitle } from '../../../hooks/use-global-state'; import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; import { CurrentPageService } from '../../../modules/page'; -import { performanceRenderLogger, WorkspaceSubPath } from '../../../shared'; +import { performanceRenderLogger } from '../../../shared'; import { PageNotFound } from '../../404'; import * as styles from './detail-page.css'; import { DetailPageHeader, RightSidebarHeader } from './detail-page-header'; @@ -117,7 +112,7 @@ const DetailPageImpl = memo(function DetailPageImpl() { const page = useService(Page); const pageRecordList = useService(PageRecordList); const currentPageId = page.id; - const { openPage, jumpToSubPath } = useNavigateHelper(); + const { openPage, jumpToTag } = useNavigateHelper(); const currentWorkspace = useService(Workspace); const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace; @@ -127,8 +122,6 @@ const DetailPageImpl = memo(function DetailPageImpl() { const isInTrash = pageMeta?.trash; - const collectionService = useService(CollectionService); - const { setTemporaryFilter } = useCollectionManager(collectionService); const mode = useLiveData(page.mode); useRegisterBlocksuiteEditorCommands(); const title = useLiveData(page.title); @@ -191,9 +184,8 @@ const DetailPageImpl = memo(function DetailPageImpl() { const dispose = editor.slots.pageLinkClicked.on(({ pageId }) => { return openPage(blockSuiteWorkspace.id, pageId); }); - const disposeTagClick = editor.slots.tagClicked.on(async ({ tagId }) => { - jumpToSubPath(currentWorkspace.id, WorkspaceSubPath.ALL); - setTemporaryFilter([createTagFilter(tagId)]); + const disposeTagClick = editor.slots.tagClicked.on(({ tagId }) => { + jumpToTag(currentWorkspace.id, tagId); }); return () => { dispose.dispose(); @@ -201,14 +193,13 @@ const DetailPageImpl = memo(function DetailPageImpl() { }; }, [ - page, - mode, - pageRecordList, - openPage, blockSuiteWorkspace.id, - jumpToSubPath, currentWorkspace.id, - setTemporaryFilter, + jumpToTag, + mode, + openPage, + page, + pageRecordList, ] ); diff --git a/packages/frontend/core/src/pages/workspace/tag.tsx b/packages/frontend/core/src/pages/workspace/tag.tsx deleted file mode 100644 index 16865c16ef..0000000000 --- a/packages/frontend/core/src/pages/workspace/tag.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { TagListHeader, useTagMetas } from '@affine/core/components/page-list'; -import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; -import { useService, Workspace } from '@toeverything/infra'; -import { useMemo } from 'react'; -import { type LoaderFunction, redirect, useParams } from 'react-router-dom'; - -import { AllPage } from './all-page/all-page'; -import { AllPageHeader } from './all-page/all-page-header'; -import { EmptyPageList } from './page-list-empty'; - -export const loader: LoaderFunction = async args => { - if (!args.params.tagId) { - return redirect('/404'); - } - - return null; -}; - -export const Component = function TagPage() { - const params = useParams(); - const currentWorkspace = useService(Workspace); - const pageMetas = useBlockSuitePageMeta(currentWorkspace.blockSuiteWorkspace); - const { tagUsageCounts } = useTagMetas( - currentWorkspace.blockSuiteWorkspace, - pageMetas - ); - const isEmpty = useMemo(() => { - if (params.tagId) { - return tagUsageCounts[params.tagId] === 0; - } - return true; - }, [params.tagId, tagUsageCounts]); - - return isEmpty ? ( - <> - - } - /> - - ) : ( - - ); -}; diff --git a/packages/frontend/core/src/pages/workspace/tag/header.tsx b/packages/frontend/core/src/pages/workspace/tag/header.tsx new file mode 100644 index 0000000000..608001612d --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/tag/header.tsx @@ -0,0 +1,23 @@ +import { Header } from '@affine/core/components/pure/header'; +import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls'; +import { WorkspaceModeFilterTab } from '@affine/core/components/pure/workspace-mode-filter-tab'; + +import * as styles from '../all-page/all-page.css'; + +export const TagDetailHeader = () => { + const isWindowsDesktop = environment.isDesktop && environment.isWindows; + + return ( +
+ {isWindowsDesktop ? : null} + + } + center={} + /> + ); +}; diff --git a/packages/frontend/core/src/pages/workspace/tag/index.tsx b/packages/frontend/core/src/pages/workspace/tag/index.tsx new file mode 100644 index 0000000000..d557841d35 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/tag/index.tsx @@ -0,0 +1,63 @@ +import { HubIsland } from '@affine/core/components/affine/hub-island'; +import { + PageListHeader, + useTagMetas, + VirtualizedPageList, +} from '@affine/core/components/page-list'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { useService } from '@toeverything/infra'; +import { Workspace } from '@toeverything/infra'; +import { useMemo } from 'react'; +import { useParams } from 'react-router-dom'; + +import { PageNotFound } from '../../404'; +import * as styles from '../all-page/all-page.css'; +import { EmptyPageList } from '../page-list-empty'; +import { TagDetailHeader } from './header'; + +export const TagDetail = ({ tagId }: { tagId?: string }) => { + const currentWorkspace = useService(Workspace); + const pageMetas = useBlockSuitePageMeta(currentWorkspace.blockSuiteWorkspace); + + const { tags, filterPageMetaByTag } = useTagMetas( + currentWorkspace.blockSuiteWorkspace, + pageMetas + ); + const tagPageMetas = useMemo(() => { + if (tagId) { + return filterPageMetaByTag(tagId); + } + return []; + }, [filterPageMetaByTag, tagId]); + + const currentTag = useMemo( + () => tags.find(tag => tag.id === tagId), + [tagId, tags] + ); + + if (!currentTag) { + return ; + } + + return ( +
+ + {tagPageMetas.length > 0 ? ( + + ) : ( + } + blockSuiteWorkspace={currentWorkspace.blockSuiteWorkspace} + /> + )} + +
+ ); +}; + +export const Component = () => { + const params = useParams(); + + return ; +}; diff --git a/packages/frontend/core/src/pages/workspace/trash-page.tsx b/packages/frontend/core/src/pages/workspace/trash-page.tsx index 409b6a1e9b..725795cd99 100644 --- a/packages/frontend/core/src/pages/workspace/trash-page.tsx +++ b/packages/frontend/core/src/pages/workspace/trash-page.tsx @@ -1,7 +1,6 @@ import { toast } from '@affine/component'; import { usePageHelper } from '@affine/core/components/blocksuite/block-suite-page-list/utils'; import { - currentCollectionAtom, type ListItem, ListTableHeader, PageListItemRenderer, @@ -19,11 +18,8 @@ import { assertExists } from '@blocksuite/global/utils'; import { DeleteIcon } from '@blocksuite/icons'; import type { PageMeta } from '@blocksuite/store'; import { Workspace } from '@toeverything/infra'; -import { getCurrentStore } from '@toeverything/infra/atom'; import { useService } from '@toeverything/infra/di'; import { useCallback } from 'react'; -import { type LoaderFunction } from 'react-router-dom'; -import { NIL } from 'uuid'; import { EmptyPageList } from './page-list-empty'; import * as styles from './trash-page.css'; @@ -50,27 +46,15 @@ const TrashHeader = () => { ); }; -export const loader: LoaderFunction = async () => { - // to fix the bug that the trash page list is not updated when route from collection to trash - // but it's not a good solution, the page will jitter when collection and trash are switched between each other. - // TODO: fix this bug - - const rootStore = getCurrentStore(); - rootStore.set(currentCollectionAtom, NIL); - return null; -}; - export const TrashPage = () => { const currentWorkspace = useService(Workspace); const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace; assertExists(blockSuiteWorkspace); const pageMetas = useBlockSuitePageMeta(blockSuiteWorkspace); - const filteredPageMetas = useFilteredPageMetas( - 'trash', - pageMetas, - currentWorkspace - ); + const filteredPageMetas = useFilteredPageMetas(currentWorkspace, pageMetas, { + trash: true, + }); const { restoreFromTrash, permanentlyDeletePage } = useBlockSuiteMetaHelper(blockSuiteWorkspace); diff --git a/packages/frontend/core/src/router.ts b/packages/frontend/core/src/router.ts index 708bb90b16..e17c1f4060 100644 --- a/packages/frontend/core/src/router.ts +++ b/packages/frontend/core/src/router.ts @@ -15,9 +15,17 @@ export const routes = [ path: 'all', lazy: () => import('./pages/workspace/all-page/all-page'), }, + { + path: 'collection', + lazy: () => import('./pages/workspace/all-collection'), + }, { path: 'collection/:collectionId', - lazy: () => import('./pages/workspace/collection'), + lazy: () => import('./pages/workspace/collection/index'), + }, + { + path: 'tag', + lazy: () => import('./pages/workspace/all-tag'), }, { path: 'tag/:tagId', diff --git a/tests/affine-local/e2e/all-page.spec.ts b/tests/affine-local/e2e/all-page.spec.ts index 868d2d1659..7c5c1b1774 100644 --- a/tests/affine-local/e2e/all-page.spec.ts +++ b/tests/affine-local/e2e/all-page.spec.ts @@ -123,6 +123,7 @@ test('allow creation of filters by tags', async ({ page }) => { await createPageWithTag(page, { title: 'Page A', tags: ['Page A'] }); await createPageWithTag(page, { title: 'Page B', tags: ['Page B'] }); await clickSideBarAllPageButton(page); + await createFirstFilter(page, 'Tags'); await checkFilterName(page, 'is not empty'); expect(await getPagesCount(page)).toBe(pagesWithTagsCount + 2); await changeFilter(page, 'contains all');