diff --git a/packages/frontend/core/src/components/affine/page-properties/info-modal/info-modal.tsx b/packages/frontend/core/src/components/affine/page-properties/info-modal/info-modal.tsx index e78a10e00ded..b613a2e5db0b 100644 --- a/packages/frontend/core/src/components/affine/page-properties/info-modal/info-modal.tsx +++ b/packages/frontend/core/src/components/affine/page-properties/info-modal/info-modal.tsx @@ -4,23 +4,16 @@ import { Modal, Scrollable, } from '@affine/component'; +import { DocInfoService } from '@affine/core/modules/doc-info'; import { DocsSearchService } from '@affine/core/modules/docs-search'; import { useI18n } from '@affine/i18n'; import { LiveData, useLiveData, - useMount, + useService, useServices, } from '@toeverything/infra'; -import { - Suspense, - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { Suspense, useCallback, useContext, useMemo, useRef } from 'react'; import { BlocksuiteHeaderTitle } from '../../../blocksuite/block-suite-header/title'; import { managerContext } from '../common'; @@ -35,35 +28,23 @@ import { LinksRow } from './links-row'; import { TagsRow } from './tags-row'; import { TimeRow } from './time-row'; -export const useInfoModal = (docId?: string) => { - const [open, setOpen] = useState(false); - const { mount } = useMount('InfoModal'); +export const InfoModal = () => { + const modal = useService(DocInfoService).modal; + const docId = useLiveData(modal.docId$); - useEffect(() => { - if (!open || !docId) return; - return mount(); - }, [docId, mount, open]); + if (!docId) return null; - return useCallback(() => setOpen(true), []); + return ; }; -/** - * For most situations, use `useInfoModal()` instead. - */ -export const InfoModal = ({ - open, - onOpenChange, - docId, -}: { - open: boolean; - onOpenChange: (open: boolean) => void; - docId: string; -}) => { +const InfoModalOpened = ({ docId }: { docId: string }) => { + const modal = useService(DocInfoService).modal; + const titleInputHandleRef = useRef(null); - const manager = usePagePropertiesManager(docId); + const manager = usePagePropertiesManager(docId ?? ''); const handleClose = useCallback(() => { - onOpenChange(false); - }, [onOpenChange]); + modal.close(); + }, [modal]); if (!manager.page || manager.readonly) { return null; @@ -74,8 +55,8 @@ export const InfoModal = ({ contentOptions={{ className: styles.container, }} - open={open} - onOpenChange={onOpenChange} + open + onOpenChange={v => modal.onOpenChange(v)} withoutCloseButton > diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header/info/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header/info/index.tsx index 1c9a54d35ed3..55936d8afc09 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header/info/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header/info/index.tsx @@ -1,18 +1,19 @@ import { IconButton } from '@affine/component'; -import { useInfoModal } from '@affine/core/components/affine/page-properties'; +import { DocInfoService } from '@affine/core/modules/doc-info'; import { useI18n } from '@affine/i18n'; import { track } from '@affine/track'; import { InformationIcon } from '@blocksuite/icons/rc'; +import { useService } from '@toeverything/infra'; import { useCallback } from 'react'; export const InfoButton = ({ docId }: { docId: string }) => { - const open = useInfoModal(docId); + const modal = useService(DocInfoService).modal; const t = useI18n(); const onOpenInfoModal = useCallback(() => { track.$.header.actions.openDocInfo(); - open(); - }, [open]); + modal.open(docId); + }, [docId, modal]); return ( { track.$.header.pageInfo.open(); - openInfo(); - }, [openInfo]); + docInfoModal.open(pageId); + }, [docInfoModal, pageId]); const handleOpenInNewTab = useCallback(() => { workbench.openDoc(pageId, { diff --git a/packages/frontend/core/src/components/hooks/affine/use-register-blocksuite-editor-commands.tsx b/packages/frontend/core/src/components/hooks/affine/use-register-blocksuite-editor-commands.tsx index d382a058acb2..917221082c60 100644 --- a/packages/frontend/core/src/components/hooks/affine/use-register-blocksuite-editor-commands.tsx +++ b/packages/frontend/core/src/components/hooks/affine/use-register-blocksuite-editor-commands.tsx @@ -3,6 +3,7 @@ import { PreconditionStrategy, registerAffineCommand, } from '@affine/core/commands'; +import { DocInfoService } from '@affine/core/modules/doc-info'; import type { Editor } from '@affine/core/modules/editor'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { WorkspaceFlavour } from '@affine/env/workspace'; @@ -19,7 +20,6 @@ import { useSetAtom } from 'jotai'; import { useCallback, useEffect } from 'react'; import { pageHistoryModalAtom } from '../../../components/atoms/page-history'; -import { useInfoModal } from '../../affine/page-properties'; import { useBlockSuiteMetaHelper } from './use-block-suite-meta-helper'; import { useExportPage } from './use-export-page'; import { useTrashModalHelper } from './use-trash-modal-helper'; @@ -36,7 +36,7 @@ export function useRegisterBlocksuiteEditorCommands(editor: Editor) { const trash = useLiveData(doc.trash$); const setPageHistoryModalState = useSetAtom(pageHistoryModalAtom); - const openInfo = useInfoModal(docId); + const docInfoModal = useService(DocInfoService).modal; const openHistoryModal = useCallback(() => { setPageHistoryModalState(() => ({ @@ -46,8 +46,8 @@ export function useRegisterBlocksuiteEditorCommands(editor: Editor) { }, [docId, setPageHistoryModalState]); const openInfoModal = useCallback(() => { - openInfo(); - }, [openInfo]); + docInfoModal.open(docId); + }, [docId, docInfoModal]); const { duplicate } = useBlockSuiteMetaHelper(); const exportHandler = useExportPage(); diff --git a/packages/frontend/core/src/components/page-list/operation-cell.tsx b/packages/frontend/core/src/components/page-list/operation-cell.tsx index 213f13fee40b..ce594d317a59 100644 --- a/packages/frontend/core/src/components/page-list/operation-cell.tsx +++ b/packages/frontend/core/src/components/page-list/operation-cell.tsx @@ -8,6 +8,7 @@ import { import { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper'; import { useTrashModalHelper } from '@affine/core/components/hooks/affine/use-trash-modal-helper'; import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook'; +import { DocInfoService } from '@affine/core/modules/doc-info'; import { FavoriteService } from '@affine/core/modules/favorite'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { WorkbenchService } from '@affine/core/modules/workbench'; @@ -32,6 +33,7 @@ import type { DocMeta } from '@blocksuite/store'; import { FeatureFlagService, useLiveData, + useService, useServices, WorkspaceService, } from '@toeverything/infra'; @@ -39,7 +41,6 @@ import type { MouseEvent } from 'react'; import { useCallback, useState } from 'react'; import type { CollectionService } from '../../modules/collection'; -import { useInfoModal } from '../affine/page-properties'; import { usePageHelper } from '../blocksuite/block-suite-page-list/utils'; import { IsFavoriteIcon } from '../pure/icons'; import { FavoriteTag } from './components/favorite-tag'; @@ -86,11 +87,11 @@ export const PageOperationCell = ({ const { duplicate } = useBlockSuiteMetaHelper(); const blocksuiteDoc = currentWorkspace.docCollection.getDoc(page.id); - const openInfoModal = useInfoModal(blocksuiteDoc?.id); + const docInfoModal = useService(DocInfoService).modal; const onOpenInfoModal = useCallback(() => { track.$.docInfoPanel.$.open(); - openInfoModal(); - }, [openInfoModal]); + docInfoModal.open(blocksuiteDoc?.id); + }, [blocksuiteDoc?.id, docInfoModal]); const onDisablePublicSharing = useCallback(() => { // TODO(@EYHN): implement disable public sharing diff --git a/packages/frontend/core/src/components/providers/modal-provider.tsx b/packages/frontend/core/src/components/providers/modal-provider.tsx index 83a26951527c..afdecc41ed5d 100644 --- a/packages/frontend/core/src/components/providers/modal-provider.tsx +++ b/packages/frontend/core/src/components/providers/modal-provider.tsx @@ -21,6 +21,7 @@ import { AuthModal } from '../affine/auth'; import { AiLoginRequiredModal } from '../affine/auth/ai-login-required'; import { HistoryTipsModal } from '../affine/history-tips-modal'; import { IssueFeedbackModal } from '../affine/issue-feedback-modal'; +import { InfoModal } from '../affine/page-properties/info-modal/info-modal'; import { CloudQuotaModal, LocalQuotaModal, @@ -126,6 +127,7 @@ export function CurrentWorkspaceModals() { onOpenChange={onTrashConfirmOpenChange} titles={deletePageTitles} /> + ); } diff --git a/packages/frontend/core/src/mobile/provider/model-provider.tsx b/packages/frontend/core/src/mobile/provider/model-provider.tsx index 606ddc00f587..07d4bc8c0a2c 100644 --- a/packages/frontend/core/src/mobile/provider/model-provider.tsx +++ b/packages/frontend/core/src/mobile/provider/model-provider.tsx @@ -2,6 +2,7 @@ import { NotificationCenter } from '@affine/component'; import { AiLoginRequiredModal } from '@affine/core/components/affine/auth/ai-login-required'; import { HistoryTipsModal } from '@affine/core/components/affine/history-tips-modal'; import { IssueFeedbackModal } from '@affine/core/components/affine/issue-feedback-modal'; +import { InfoModal } from '@affine/core/components/affine/page-properties/info-modal/info-modal'; import { CloudQuotaModal, LocalQuotaModal, @@ -57,6 +58,7 @@ export function MobileCurrentWorkspaceModals() { onOpenChange={onTrashConfirmOpenChange} titles={deletePageTitles} /> + ); } diff --git a/packages/frontend/core/src/modules/doc-info/entities/modal.ts b/packages/frontend/core/src/modules/doc-info/entities/modal.ts new file mode 100644 index 000000000000..c596847724ed --- /dev/null +++ b/packages/frontend/core/src/modules/doc-info/entities/modal.ts @@ -0,0 +1,22 @@ +import { Entity, LiveData } from '@toeverything/infra'; + +export class DocInfoModal extends Entity { + public readonly docId$ = new LiveData(null); + public readonly open$ = LiveData.computed(get => !!get(this.docId$)); + + public open(docId?: string) { + if (docId) { + this.docId$.next(docId); + } else { + this.docId$.next(null); + } + } + + public close() { + this.docId$.next(null); + } + + public onOpenChange(open: boolean) { + if (!open) this.docId$.next(null); + } +} diff --git a/packages/frontend/core/src/modules/doc-info/index.ts b/packages/frontend/core/src/modules/doc-info/index.ts new file mode 100644 index 000000000000..14d8afa3df58 --- /dev/null +++ b/packages/frontend/core/src/modules/doc-info/index.ts @@ -0,0 +1,10 @@ +import { type Framework, WorkspaceScope } from '@toeverything/infra'; + +import { DocInfoModal } from './entities/modal'; +import { DocInfoService } from './services/doc-info'; + +export { DocInfoService }; + +export function configureDocInfoModule(framework: Framework) { + framework.scope(WorkspaceScope).service(DocInfoService).entity(DocInfoModal); +} diff --git a/packages/frontend/core/src/modules/doc-info/services/doc-info.ts b/packages/frontend/core/src/modules/doc-info/services/doc-info.ts new file mode 100644 index 000000000000..1ff35f9b2a37 --- /dev/null +++ b/packages/frontend/core/src/modules/doc-info/services/doc-info.ts @@ -0,0 +1,7 @@ +import { Service } from '@toeverything/infra'; + +import { DocInfoModal } from '../entities/modal'; + +export class DocInfoService extends Service { + public readonly modal = this.framework.createEntity(DocInfoModal); +} diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx index 95f46a54e9bf..6e15bf32ccce 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx +++ b/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx @@ -5,9 +5,9 @@ import { toast, Tooltip, } from '@affine/component'; -import { useInfoModal } from '@affine/core/components/affine/page-properties'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta'; +import { DocInfoService } from '@affine/core/modules/doc-info'; import { DocsSearchService } from '@affine/core/modules/docs-search'; import type { AffineDNDData } from '@affine/core/types/dnd'; import { useI18n } from '@affine/i18n'; @@ -17,6 +17,7 @@ import { GlobalContextService, LiveData, useLiveData, + useService, useServices, } from '@toeverything/infra'; import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; @@ -175,15 +176,15 @@ export const ExplorerDocNode = ({ [canDrop] ); - const openInfoModal = useInfoModal(docId); + const docInfoModal = useService(DocInfoService).modal; const operations = useExplorerDocNodeOperations( docId, useMemo( () => ({ - openInfoModal: () => openInfoModal(), + openInfoModal: () => docInfoModal.open(docId), openNodeCollapsed: () => setCollapsed(false), }), - [openInfoModal] + [docId, docInfoModal] ) ); diff --git a/packages/frontend/core/src/modules/index.ts b/packages/frontend/core/src/modules/index.ts index 24dc1d489d48..3d4ebc151a14 100644 --- a/packages/frontend/core/src/modules/index.ts +++ b/packages/frontend/core/src/modules/index.ts @@ -5,6 +5,7 @@ import { configureCloudModule } from './cloud'; import { configureCollectionModule } from './collection'; import { configureCreateWorkspaceModule } from './create-workspace'; import { configureDocDisplayMetaModule } from './doc-display-meta'; +import { configureDocInfoModule } from './doc-info'; import { configureDocLinksModule } from './doc-link'; import { configureDocsSearchModule } from './docs-search'; import { configureEditorModule } from './editor'; @@ -55,4 +56,5 @@ export function configureCommonModules(framework: Framework) { configureImportTemplateModule(framework); configureCreateWorkspaceModule(framework); configureUserspaceModule(framework); + configureDocInfoModule(framework); }