feat(core): focus on text body when opening journal (#14122)

Related to issue https://github.com/toeverything/AFFiNE/issues/14094

This PR makes it so that focus is put on the input body when loading a
journal. A check is made when loading the document whether it is a
normal document or a journal document. If it is a journal document, the
last noteblock in the document is focused on. This does not change how
the title is focused on normal documents.

This makes it more effortless to use the journal, as you don't have to
click on the body of the journal after opening/creating it.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Improved editor focus for journal documents: when opening or switching
to a journal the cursor now auto-positions to the end of the last note
entry (or the input area) after a short, smooth delay for faster typing
and reliable focus behavior.

* **Bug Fixes**
* Added safeguards and error handling to make automatic focus more
robust across load and editor states.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Daniel Dybing
2025-12-20 07:45:40 +01:00
committed by GitHub
parent caeec23ec6
commit 28a1ac4772

View File

@@ -22,6 +22,7 @@ import { DocService } from '@affine/core/modules/doc';
import { EditorService } from '@affine/core/modules/editor';
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
import { GlobalContextService } from '@affine/core/modules/global-context';
import { JournalService } from '@affine/core/modules/journal';
import { PeekViewService } from '@affine/core/modules/peek-view';
import { RecentDocsService } from '@affine/core/modules/quicksearch';
import {
@@ -38,6 +39,8 @@ import { ServerFeature } from '@affine/graphql';
import track from '@affine/track';
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference';
import { focusBlockEnd } from '@blocksuite/affine/shared/commands';
import { getLastNoteBlock } from '@blocksuite/affine/shared/utils';
import {
AiIcon,
CommentIcon,
@@ -184,10 +187,37 @@ const DetailPageImpl = memo(function DetailPageImpl() {
useRegisterBlocksuiteEditorCommands(editor, isActiveView);
const journalService = useService(JournalService);
const isJournal = !!useLiveData(journalService.journalDate$(doc.id));
const onLoad = useCallback(
(editorContainer: AffineEditorContainer) => {
const std = editorContainer.std;
const disposable = new DisposableGroup();
// Check if journal and handle accordingly to set focus on input block.
if (isJournal) {
const rafId = requestAnimationFrame(() => {
try {
if (!editorContainer.isConnected) return;
const page = editorContainer.page;
const note = getLastNoteBlock(page);
const std = editorContainer.std;
if (note) {
const lastBlock = note.lastChild();
if (lastBlock) {
const focusBlock = std.view.getBlock(lastBlock.id) ?? undefined;
std.command.exec(focusBlockEnd, { focusBlock, force: true });
return;
}
}
std.command.exec(focusBlockEnd, { force: true });
} catch (error) {
console.error('Failed to focus journal body', error);
}
});
disposable.add(() => cancelAnimationFrame(rafId));
}
if (std) {
const refNodeSlots = std.getOptional(RefNodeSlotsProvider);
if (refNodeSlots) {
@@ -265,7 +295,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
disposable.dispose();
};
},
[editor, workbench, peekView]
[editor, workbench, peekView, isJournal]
);
const [hasScrollTop, setHasScrollTop] = useState(false);