fix(core): correctly toggle visibility of starter-bar based on doc.isEmpty (#10439)

This commit is contained in:
CatsJuice
2025-02-26 07:49:50 +00:00
parent 866b096304
commit e1fd8f5d80
4 changed files with 64 additions and 10 deletions
@@ -1,6 +1,12 @@
import { assertType, type Constructor, Slot } from '@blocksuite/global/utils';
import {
assertType,
type Constructor,
DisposableGroup,
Slot,
} from '@blocksuite/global/utils';
import type { Boxed } from '@blocksuite/store';
import { BlockModel, nanoid } from '@blocksuite/store';
import { signal } from '@preact/signals-core';
import * as Y from 'yjs';
import {
@@ -98,6 +104,8 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
oldValues: Record<string, unknown>;
}>();
private readonly _isEmpty$ = signal(false);
get elementModels() {
const models: GfxPrimitiveElementModel[] = [];
this._elementModels.forEach(model => models.push(model.model));
@@ -113,7 +121,7 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
}
override isEmpty(): boolean {
return this._elementModels.size === 0 && this.children.length === 0;
return this._isEmpty$.value;
}
constructor() {
@@ -370,6 +378,7 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
if (isGfxGroupCompatibleModel(payload.model)) {
this._groupLikeModels.set(payload.id, payload.model);
}
break;
case 'delete':
if (isGfxGroupCompatibleModel(payload.model)) {
@@ -382,6 +391,7 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
group.removeChild(payload.model as GfxModel);
}
}
break;
}
});
@@ -445,6 +455,25 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
});
}
private _watchChildrenChange() {
const updateIsEmpty = () => {
this._isEmpty$.value =
this._elementModels.size === 0 && this.children.length === 0;
};
const disposables = new DisposableGroup();
disposables.add(this.elementAdded.on(updateIsEmpty));
disposables.add(this.elementRemoved.on(updateIsEmpty));
this.doc.slots.blockUpdated.on(payload => {
if (['add', 'delete'].includes(payload.type)) {
updateIsEmpty();
}
});
this.deleted.on(() => {
disposables.dispose();
});
}
protected _extendElement(
ctorMap: Record<
string,
@@ -460,6 +489,7 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
protected _init() {
this._initElementModels();
this._watchGroupRelationChange();
this._watchChildrenChange();
}
getConstructor(type: string) {
@@ -1,7 +1,7 @@
import { Container, type ServiceProvider } from '@blocksuite/global/di';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { type Disposable, Slot } from '@blocksuite/global/utils';
import { signal } from '@preact/signals-core';
import { computed, signal } from '@preact/signals-core';
import type { ExtensionType } from '../../extension/extension.js';
import { StoreSelectionExtension } from '../../extension/index.js';
@@ -55,6 +55,10 @@ export class Store {
private readonly _readonly = signal(false);
private readonly _isEmpty = computed(() => {
return this.root?.isEmpty() ?? true;
});
private readonly _schema: Schema;
readonly slots: Doc['slots'] & {
@@ -215,7 +219,11 @@ export class Store {
}
get isEmpty() {
return this.root?.isEmpty() ?? true;
return this._isEmpty.peek();
}
get isEmpty$() {
return this._isEmpty;
}
get loaded() {
@@ -115,7 +115,7 @@ const StarterBarNotEmpty = ({ doc }: { doc: Store }) => {
}
return (
<div className={styles.root}>
<div className={styles.root} data-testid="starter-bar">
{t['com.affine.page-starter-bar.start']()}
<ul className={styles.badges}>
{enableAI ? (
@@ -166,12 +166,9 @@ export const StarterBar = ({ doc }: { doc: Store }) => {
);
useEffect(() => {
const disposable = doc.slots.blockUpdated.on(() => {
setIsEmpty(doc.isEmpty);
return doc.isEmpty$.subscribe(value => {
setIsEmpty(value);
});
return () => {
disposable.dispose();
};
}, [doc]);
if (!isEmpty || isTemplate) return null;
+19
View File
@@ -251,3 +251,22 @@ test('create template doc from sidebar template entrance', async ({ page }) => {
page.getByTestId(`template-doc-item-${templateDocId}`)
).toBeVisible();
});
test('should show starter-bar when doc is empty', async ({ page }) => {
await openHomePage(page);
await page.getByTestId('sidebar-new-page-button').click();
await page.keyboard.press('ArrowDown');
const starterBar = page.getByTestId('starter-bar');
await expect(starterBar).toBeVisible();
await page.keyboard.type('1', { delay: 100 });
await expect(starterBar).not.toBeVisible();
await page.keyboard.type('1', { delay: 100 });
await expect(starterBar).not.toBeVisible();
await page.keyboard.press('Backspace');
await expect(starterBar).not.toBeVisible();
await page.keyboard.press('Backspace');
await expect(starterBar).toBeVisible();
await page.keyboard.type('1', { delay: 100 });
await expect(starterBar).not.toBeVisible();
});