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 ddc5e438bd..b613a2e5db 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,9 +4,15 @@ 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, useServices } from '@toeverything/infra'; +import { + LiveData, + useLiveData, + useService, + useServices, +} from '@toeverything/infra'; import { Suspense, useCallback, useContext, useMemo, useRef } from 'react'; import { BlocksuiteHeaderTitle } from '../../../blocksuite/block-suite-header/title'; @@ -22,20 +28,23 @@ import { LinksRow } from './links-row'; import { TagsRow } from './tags-row'; import { TimeRow } from './time-row'; -export const InfoModal = ({ - open, - onOpenChange, - docId, -}: { - open: boolean; - onOpenChange: (open: boolean) => void; - docId: string; -}) => { +export const InfoModal = () => { + const modal = useService(DocInfoService).modal; + const docId = useLiveData(modal.docId$); + + if (!docId) return null; + + return ; +}; + +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; @@ -46,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/atoms/index.ts b/packages/frontend/core/src/components/atoms/index.ts index c519e959ff..7ffbf3f36c 100644 --- a/packages/frontend/core/src/components/atoms/index.ts +++ b/packages/frontend/core/src/components/atoms/index.ts @@ -12,7 +12,6 @@ export const openQuotaModalAtom = atom(false); export const openStarAFFiNEModalAtom = atom(false); export const openIssueFeedbackModalAtom = atom(false); export const openHistoryTipsModalAtom = atom(false); -export const openInfoModalAtom = atom(false); export const rightSidebarWidthAtom = atom(320); 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 1474241c51..55936d8afc 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,19 +1,19 @@ import { IconButton } from '@affine/component'; -import { openInfoModalAtom } from '@affine/core/components/atoms'; +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 { useSetAtom } from 'jotai'; +import { useService } from '@toeverything/infra'; import { useCallback } from 'react'; -export const InfoButton = () => { - const setOpenInfoModal = useSetAtom(openInfoModalAtom); +export const InfoButton = ({ docId }: { docId: string }) => { + const modal = useService(DocInfoService).modal; const t = useI18n(); const onOpenInfoModal = useCallback(() => { track.$.header.actions.openDocInfo(); - setOpenInfoModal(true); - }, [setOpenInfoModal]); + modal.open(docId); + }, [docId, modal]); return ( { track.$.header.pageInfo.open(); - setOpenInfoModal(true); - }, [setOpenInfoModal]); + 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 116e3fd125..917221082c 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,7 +3,7 @@ import { PreconditionStrategy, registerAffineCommand, } from '@affine/core/commands'; -import { openInfoModalAtom } from '@affine/core/components/atoms'; +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'; @@ -36,7 +36,7 @@ export function useRegisterBlocksuiteEditorCommands(editor: Editor) { const trash = useLiveData(doc.trash$); const setPageHistoryModalState = useSetAtom(pageHistoryModalAtom); - const setInfoModalState = useSetAtom(openInfoModalAtom); + const docInfoModal = useService(DocInfoService).modal; const openHistoryModal = useCallback(() => { setPageHistoryModalState(() => ({ @@ -46,8 +46,8 @@ export function useRegisterBlocksuiteEditorCommands(editor: Editor) { }, [docId, setPageHistoryModalState]); const openInfoModal = useCallback(() => { - setInfoModalState(true); - }, [setInfoModalState]); + 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 9ca95c3d80..36d02a986a 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 { 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 { InfoModal } 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, setOpenInfoModal] = useState(false); + const docInfoModal = useService(DocInfoService).modal; const onOpenInfoModal = useCallback(() => { track.$.docInfoPanel.$.open(); - setOpenInfoModal(true); - }, []); + docInfoModal.open(blocksuiteDoc?.id); + }, [blocksuiteDoc?.id, docInfoModal]); const onDisablePublicSharing = useCallback(() => { // TODO(@EYHN): implement disable public sharing @@ -214,13 +215,6 @@ export const PageOperationCell = ({ - {blocksuiteDoc ? ( - - ) : null} ); }; diff --git a/packages/frontend/core/src/components/providers/modal-provider.tsx b/packages/frontend/core/src/components/providers/modal-provider.tsx index 83a2695152..afdecc41ed 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/desktop/pages/workspace/detail-page/detail-page-header.tsx b/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page-header.tsx index dbefe6a8c8..a8df2f9f81 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page-header.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page-header.tsx @@ -3,8 +3,6 @@ import { type InlineEditHandle, observeResize, } from '@affine/component'; -import { InfoModal } from '@affine/core/components/affine/page-properties'; -import { openInfoModalAtom } from '@affine/core/components/atoms'; import { FavoriteButton } from '@affine/core/components/blocksuite/block-suite-header/favorite'; import { InfoButton } from '@affine/core/components/blocksuite/block-suite-header/info'; import { JournalWeekDatePicker } from '@affine/core/components/blocksuite/block-suite-header/journal/date-picker'; @@ -19,7 +17,7 @@ import { EditorService } from '@affine/core/modules/editor'; import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench'; import type { Doc } from '@blocksuite/affine/store'; import { useLiveData, useService, type Workspace } from '@toeverything/infra'; -import { useAtom, useAtomValue } from 'jotai'; +import { useAtomValue } from 'jotai'; import { forwardRef, useCallback, useEffect, useRef, useState } from 'react'; import { SharePageButton } from '../../../../components/affine/share-page-modal'; @@ -139,7 +137,7 @@ export function NormalPageHeader({ page, workspace }: PageHeaderProps) { {hideCollect ? null : ( <> - + )} - {isJournal && !isInTrash ? ( - - ) : ( - - )} - - + return isJournal && !isInTrash ? ( + + ) : ( + ); } diff --git a/packages/frontend/core/src/mobile/provider/model-provider.tsx b/packages/frontend/core/src/mobile/provider/model-provider.tsx index 606ddc00f5..07d4bc8c0a 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 0000000000..c596847724 --- /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 0000000000..14d8afa3df --- /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 0000000000..1ff35f9b2a --- /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 2774f009da..6e15bf32cc 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 { InfoModal } 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 [enableInfoModal, setEnableInfoModal] = useState(false); + const docInfoModal = useService(DocInfoService).modal; const operations = useExplorerDocNodeOperations( docId, useMemo( () => ({ - openInfoModal: () => setEnableInfoModal(true), + openInfoModal: () => docInfoModal.open(docId), openNodeCollapsed: () => setCollapsed(false), }), - [] + [docId, docInfoModal] ) ); @@ -199,57 +200,48 @@ export const ExplorerDocNode = ({ } return ( - <> - -
- -
- - ) - } - reorderable={reorderable} - onRename={handleRename} - childrenPlaceholder={} - operations={finalOperations} - dropEffect={handleDropEffectOnDoc} - data-testid={`explorer-doc-${docId}`} - > - {children?.map(child => ( - - ))} -
- {enableInfoModal && ( - +
+ +
+ + ) + } + reorderable={reorderable} + onRename={handleRename} + childrenPlaceholder={} + operations={finalOperations} + dropEffect={handleDropEffectOnDoc} + data-testid={`explorer-doc-${docId}`} + > + {children?.map(child => ( + - )} - + ))} + ); }; diff --git a/packages/frontend/core/src/modules/index.ts b/packages/frontend/core/src/modules/index.ts index 24dc1d489d..3d4ebc151a 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); }