mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
@@ -2,8 +2,8 @@ import { Loading, Scrollable } from '@affine/component';
|
||||
import { EditorLoading } from '@affine/component/page-detail-skeleton';
|
||||
import { Button, IconButton } from '@affine/component/ui/button';
|
||||
import { Modal, useConfirmModal } from '@affine/component/ui/modal';
|
||||
import { useDocCollectionPageTitle } from '@affine/core/components/hooks/use-block-suite-workspace-page-title';
|
||||
import { GlobalDialogService } from '@affine/core/modules/dialogs';
|
||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { WorkspacePermissionService } from '@affine/core/modules/permissions';
|
||||
import { WorkspaceQuotaService } from '@affine/core/modules/quota';
|
||||
@@ -433,7 +433,10 @@ const PageHistoryManager = ({
|
||||
const editor = useService(EditorService).editor;
|
||||
const [mode, setMode] = useState<DocMode>(editor.mode$.value);
|
||||
|
||||
const title = useDocCollectionPageTitle(docCollection, pageId);
|
||||
const docDisplayMetaService = useService(DocDisplayMetaService);
|
||||
const i18n = useI18n();
|
||||
|
||||
const title = useLiveData(docDisplayMetaService.title$(pageId));
|
||||
|
||||
const onConfirmRestore = useCallback(() => {
|
||||
openConfirmModal({
|
||||
@@ -467,7 +470,7 @@ const PageHistoryManager = ({
|
||||
snapshotPage={snapshotPage}
|
||||
mode={mode}
|
||||
onModeChange={setMode}
|
||||
title={title}
|
||||
title={i18n.t(title)}
|
||||
/>
|
||||
|
||||
<PageHistoryList
|
||||
|
||||
@@ -3,7 +3,6 @@ import { style, type StyleRule } from '@vanilla-extract/css';
|
||||
|
||||
export const docEditorRoot = style({
|
||||
display: 'block',
|
||||
background: cssVar('backgroundPrimaryColor'),
|
||||
overflowX: 'clip',
|
||||
});
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import { assertExists } from '@blocksuite/affine/global/utils';
|
||||
import type { DocCollection } from '@blocksuite/affine/store';
|
||||
import type { Atom } from 'jotai';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useJournalInfoHelper } from './use-journal';
|
||||
|
||||
const weakMap = new WeakMap<DocCollection, Map<string, Atom<string>>>();
|
||||
|
||||
function getAtom(w: DocCollection, pageId: string): Atom<string> {
|
||||
if (!weakMap.has(w)) {
|
||||
weakMap.set(w, new Map());
|
||||
}
|
||||
const map = weakMap.get(w);
|
||||
assertExists(map);
|
||||
if (!map.has(pageId)) {
|
||||
const baseAtom = atom<string>(w.getDoc(pageId)?.meta?.title || 'Untitled');
|
||||
baseAtom.onMount = set => {
|
||||
const disposable = w.meta.docMetaUpdated.on(() => {
|
||||
const page = w.getDoc(pageId);
|
||||
set(page?.meta?.title || 'Untitled');
|
||||
});
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
};
|
||||
};
|
||||
map.set(pageId, baseAtom);
|
||||
return baseAtom;
|
||||
} else {
|
||||
return map.get(pageId) as Atom<string>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `useDocTitle(docId: string)` instead
|
||||
*/
|
||||
export function useDocCollectionPageTitle(
|
||||
docCollection: DocCollection,
|
||||
pageId: string
|
||||
) {
|
||||
const titleAtom = getAtom(docCollection, pageId);
|
||||
assertExists(titleAtom);
|
||||
const title = useAtomValue(titleAtom);
|
||||
const { localizedJournalDate } = useJournalInfoHelper(pageId);
|
||||
return localizedJournalDate || title;
|
||||
}
|
||||
|
||||
// This hook is NOT reactive to the page title change
|
||||
export function useGetDocCollectionPageTitle(docCollection: DocCollection) {
|
||||
const { getLocalizedJournalDateString } = useJournalInfoHelper();
|
||||
return useCallback(
|
||||
(pageId: string) => {
|
||||
return (
|
||||
getLocalizedJournalDateString(pageId) ||
|
||||
docCollection.getDoc(pageId)?.meta?.title
|
||||
);
|
||||
},
|
||||
[docCollection, getLocalizedJournalDateString]
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const root = style({
|
||||
position: 'relative',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
export const header = style({
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
@@ -24,3 +30,18 @@ export const iconButtonContainer = style({
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
});
|
||||
|
||||
export const dragHandle = style({
|
||||
cursor: 'grab',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: -16,
|
||||
width: 16,
|
||||
opacity: 0,
|
||||
selectors: {
|
||||
[`${root}:hover &, ${root}[data-dragging="true"] &`]: {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
Divider,
|
||||
DragHandle,
|
||||
type InlineEditHandle,
|
||||
observeResize,
|
||||
useDraggable,
|
||||
} from '@affine/component';
|
||||
import { SharePageButton } from '@affine/core/components/affine/share-page-modal';
|
||||
import { FavoriteButton } from '@affine/core/components/blocksuite/block-suite-header/favorite';
|
||||
@@ -13,11 +15,13 @@ import { DetailPageHeaderPresentButton } from '@affine/core/components/blocksuit
|
||||
import { BlocksuiteHeaderTitle } from '@affine/core/components/blocksuite/block-suite-header/title';
|
||||
import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch';
|
||||
import { useRegisterCopyLinkCommands } from '@affine/core/components/hooks/affine/use-register-copy-link-commands';
|
||||
import { useDocCollectionPageTitle } from '@affine/core/components/hooks/use-block-suite-workspace-page-title';
|
||||
import { HeaderDivider } from '@affine/core/components/pure/header';
|
||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { JournalService } from '@affine/core/modules/journal';
|
||||
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
|
||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import type { Doc } from '@blocksuite/affine/store';
|
||||
import { useLiveData, useService, type Workspace } from '@toeverything/infra';
|
||||
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
||||
@@ -60,7 +64,11 @@ export function JournalPageHeader({ page, workspace }: PageHeaderProps) {
|
||||
|
||||
const { hideShare, hideToday } =
|
||||
useDetailPageHeaderResponsive(containerWidth);
|
||||
const title = useDocCollectionPageTitle(workspace.docCollection, page?.id);
|
||||
|
||||
const docDisplayMetaService = useService(DocDisplayMetaService);
|
||||
const i18n = useI18n();
|
||||
const title = i18n.t(useLiveData(docDisplayMetaService.title$(page.id)));
|
||||
|
||||
return (
|
||||
<Header className={styles.header} ref={containerRef}>
|
||||
<ViewTitle title={title} />
|
||||
@@ -106,7 +114,10 @@ export function NormalPageHeader({ page, workspace }: PageHeaderProps) {
|
||||
);
|
||||
}, []);
|
||||
|
||||
const title = useDocCollectionPageTitle(workspace.docCollection, page?.id);
|
||||
const docDisplayMetaService = useService(DocDisplayMetaService);
|
||||
const i18n = useI18n();
|
||||
const title = i18n.t(useLiveData(docDisplayMetaService.title$(page.id)));
|
||||
|
||||
const editor = useService(EditorService).editor;
|
||||
const currentMode = useLiveData(editor.mode$);
|
||||
|
||||
@@ -148,8 +159,12 @@ export function NormalPageHeader({ page, workspace }: PageHeaderProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export function DetailPageHeader(props: PageHeaderProps) {
|
||||
const { page, workspace } = props;
|
||||
export function DetailPageHeader(
|
||||
props: PageHeaderProps & {
|
||||
onDragging?: (dragging: boolean) => void;
|
||||
}
|
||||
) {
|
||||
const { page, workspace, onDragging } = props;
|
||||
const journalService = useService(JournalService);
|
||||
const isJournal = !!useLiveData(journalService.journalDate$(page.id));
|
||||
const isInTrash = page.meta?.trash;
|
||||
@@ -159,9 +174,42 @@ export function DetailPageHeader(props: PageHeaderProps) {
|
||||
docId: page.id,
|
||||
});
|
||||
|
||||
return isJournal && !isInTrash ? (
|
||||
<JournalPageHeader {...props} />
|
||||
) : (
|
||||
<NormalPageHeader {...props} />
|
||||
const { dragRef, dragHandleRef, dragging } =
|
||||
useDraggable<AffineDNDData>(() => {
|
||||
return {
|
||||
data: {
|
||||
from: {
|
||||
at: 'doc-detail:header',
|
||||
docId: page.id,
|
||||
},
|
||||
entity: {
|
||||
type: 'doc',
|
||||
id: page.id,
|
||||
},
|
||||
},
|
||||
disableDragPreview: true,
|
||||
};
|
||||
}, [page.id]);
|
||||
|
||||
const inner =
|
||||
isJournal && !isInTrash ? (
|
||||
<JournalPageHeader {...props} />
|
||||
) : (
|
||||
<NormalPageHeader {...props} />
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onDragging?.(dragging);
|
||||
}, [dragging, onDragging]);
|
||||
|
||||
return (
|
||||
<div className={styles.root} ref={dragRef} data-dragging={dragging}>
|
||||
<DragHandle
|
||||
ref={dragHandleRef}
|
||||
dragging={dragging}
|
||||
className={styles.dragHandle}
|
||||
/>
|
||||
{inner}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const mainContainer = style({
|
||||
@@ -39,6 +40,11 @@ export const affineDocViewport = style({
|
||||
zIndex: -1,
|
||||
},
|
||||
},
|
||||
selectors: {
|
||||
'&[data-dragging="true"]': {
|
||||
backgroundColor: cssVarV2.layer.background.hoverOverlay,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const scrollbar = style({
|
||||
|
||||
@@ -249,10 +249,16 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
setHasScrollTop(hasScrollTop);
|
||||
}, []);
|
||||
|
||||
const [dragging, setDragging] = useState(false);
|
||||
|
||||
return (
|
||||
<FrameworkScope scope={editor.scope}>
|
||||
<ViewHeader>
|
||||
<DetailPageHeader page={doc.blockSuiteDoc} workspace={workspace} />
|
||||
<DetailPageHeader
|
||||
page={doc.blockSuiteDoc}
|
||||
workspace={workspace}
|
||||
onDragging={setDragging}
|
||||
/>
|
||||
</ViewHeader>
|
||||
<ViewBody>
|
||||
<div
|
||||
@@ -267,6 +273,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
<Scrollable.Viewport
|
||||
onScroll={handleScroll}
|
||||
ref={scrollViewportRef}
|
||||
data-dragging={dragging}
|
||||
className={clsx(
|
||||
'affine-page-viewport',
|
||||
styles.affineDocViewport,
|
||||
|
||||
@@ -75,6 +75,10 @@ export interface AffineDNDData extends DNDData {
|
||||
at: 'doc-property:manager';
|
||||
workspaceId: string;
|
||||
}
|
||||
| {
|
||||
at: 'doc-detail:header';
|
||||
docId: string;
|
||||
}
|
||||
| {
|
||||
at: 'external'; // for blocksuite or external apps
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user