mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 21:41:52 +08:00
feat(editor): replace slot with rxjs subject (#10768)
This commit is contained in:
@@ -441,7 +441,7 @@ const CREATE_AS_DOC = {
|
||||
newDoc.addBlock('affine:surface', {}, rootId);
|
||||
const noteId = newDoc.addBlock('affine:note', {}, rootId);
|
||||
|
||||
host.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.emit({
|
||||
host.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.next({
|
||||
pageId: newDoc.id,
|
||||
host,
|
||||
});
|
||||
|
||||
@@ -341,7 +341,7 @@ const OthersAIGroup: AIItemGroupConfig = {
|
||||
icon: CommentIcon(),
|
||||
handler: host => {
|
||||
const panel = getAIPanelWidget(host);
|
||||
AIProvider.slots.requestOpenWithChat.emit({
|
||||
AIProvider.slots.requestOpenWithChat.next({
|
||||
host,
|
||||
autoSelect: true,
|
||||
});
|
||||
|
||||
@@ -565,7 +565,7 @@ export function actionToResponse<T extends keyof BlockSuitePresets.AIActions>(
|
||||
handler: () => {
|
||||
reportResponse('result:continue-in-chat');
|
||||
const panel = getAIPanelWidget(host);
|
||||
AIProvider.slots.requestOpenWithChat.emit({ host });
|
||||
AIProvider.slots.requestOpenWithChat.next({ host });
|
||||
panel.hide();
|
||||
},
|
||||
},
|
||||
@@ -604,11 +604,11 @@ export function actionToErrorResponse<
|
||||
): ErrorConfig {
|
||||
return {
|
||||
upgrade: () => {
|
||||
AIProvider.slots.requestUpgradePlan.emit({ host: panel.host });
|
||||
AIProvider.slots.requestUpgradePlan.next({ host: panel.host });
|
||||
panel.hide();
|
||||
},
|
||||
login: () => {
|
||||
AIProvider.slots.requestLogin.emit({ host: panel.host });
|
||||
AIProvider.slots.requestLogin.next({ host: panel.host });
|
||||
panel.hide();
|
||||
},
|
||||
cancel: () => {
|
||||
|
||||
@@ -193,7 +193,7 @@ function buildPageResponseConfig<T extends keyof BlockSuitePresets.AIActions>(
|
||||
icon: ChatWithAiIcon(),
|
||||
handler: () => {
|
||||
reportResponse('result:continue-in-chat');
|
||||
AIProvider.slots.requestOpenWithChat.emit({ host });
|
||||
AIProvider.slots.requestOpenWithChat.next({ host });
|
||||
panel.hide();
|
||||
},
|
||||
},
|
||||
@@ -258,11 +258,11 @@ export function buildFinishConfig<T extends keyof BlockSuitePresets.AIActions>(
|
||||
export function buildErrorConfig(panel: AffineAIPanelWidget) {
|
||||
return {
|
||||
upgrade: () => {
|
||||
AIProvider.slots.requestUpgradePlan.emit({ host: panel.host });
|
||||
AIProvider.slots.requestUpgradePlan.next({ host: panel.host });
|
||||
panel.hide();
|
||||
},
|
||||
login: () => {
|
||||
AIProvider.slots.requestLogin.emit({ host: panel.host });
|
||||
AIProvider.slots.requestLogin.next({ host: panel.host });
|
||||
panel.hide();
|
||||
},
|
||||
cancel: () => {
|
||||
|
||||
@@ -273,12 +273,16 @@ export class ChatPanelInput extends SignalWatcher(WithDisposable(LitElement)) {
|
||||
super.connectedCallback();
|
||||
|
||||
this._disposables.add(
|
||||
AIProvider.slots.requestSendWithChat.on(
|
||||
async ({ input, context, host }) => {
|
||||
AIProvider.slots.requestSendWithChat.subscribe(
|
||||
({ input, context, host }) => {
|
||||
if (this.host === host) {
|
||||
context && this.updateContext(context);
|
||||
await this.updateComplete;
|
||||
await this.send(input);
|
||||
const { updateComplete, send } = this;
|
||||
updateComplete
|
||||
.then(() => {
|
||||
return send(input);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -316,7 +316,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
|
||||
.catch(console.error);
|
||||
|
||||
disposables.add(
|
||||
AIProvider.slots.userInfo.on(userInfo => {
|
||||
AIProvider.slots.userInfo.subscribe(userInfo => {
|
||||
const { status, error } = this.chatContextValue;
|
||||
this.avatarUrl = userInfo?.avatarUrl ?? '';
|
||||
if (
|
||||
@@ -329,7 +329,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
|
||||
})
|
||||
);
|
||||
disposables.add(
|
||||
this.host.selection.slots.changed.on(() => {
|
||||
this.host.selection.slots.changed.subscribe(() => {
|
||||
this._selectionValue = this.host.selection.value;
|
||||
})
|
||||
);
|
||||
|
||||
@@ -54,7 +54,7 @@ export class ChatPanelDocChip extends SignalWatcher(
|
||||
const doc = this.docDisplayConfig.getDoc(this.chip.docId);
|
||||
if (doc) {
|
||||
this.disposables.add(
|
||||
doc.slots.blockUpdated.on(
|
||||
doc.slots.blockUpdated.subscribe(
|
||||
throttle(this.autoUpdateChip, EXTRACT_DOC_THROTTLE)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -392,7 +392,7 @@ export class ChatPanel extends SignalWatcher(
|
||||
if (!this.doc) throw new Error('doc is required');
|
||||
|
||||
this._disposables.add(
|
||||
AIProvider.slots.actions.on(({ event }) => {
|
||||
AIProvider.slots.actions.subscribe(({ event }) => {
|
||||
const { status } = this.chatContextValue;
|
||||
if (
|
||||
event === 'finished' &&
|
||||
@@ -403,16 +403,19 @@ export class ChatPanel extends SignalWatcher(
|
||||
})
|
||||
);
|
||||
this._disposables.add(
|
||||
AIProvider.slots.userInfo.on(async () => {
|
||||
await this._initPanel();
|
||||
AIProvider.slots.userInfo.subscribe(() => {
|
||||
this._initPanel().catch(console.error);
|
||||
})
|
||||
);
|
||||
this._disposables.add(
|
||||
AIProvider.slots.requestOpenWithChat.on(async ({ host }) => {
|
||||
AIProvider.slots.requestOpenWithChat.subscribe(({ host }) => {
|
||||
if (this.host === host) {
|
||||
const context = await extractSelectedContent(host);
|
||||
if (!context) return;
|
||||
this.updateContext(context);
|
||||
extractSelectedContent(host)
|
||||
.then(context => {
|
||||
if (!context) return;
|
||||
this.updateContext(context);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ export const AIPreloadConfig = [
|
||||
icon: LanguageIcon(),
|
||||
text: 'Read a foreign language article with AI',
|
||||
handler: () => {
|
||||
AIProvider.slots.requestInsertTemplate.emit({
|
||||
AIProvider.slots.requestInsertTemplate.next({
|
||||
template: readAforeign,
|
||||
mode: 'edgeless',
|
||||
});
|
||||
@@ -28,7 +28,7 @@ export const AIPreloadConfig = [
|
||||
icon: MindmapIcon(),
|
||||
text: 'Tidy an article with AI MindMap Action',
|
||||
handler: () => {
|
||||
AIProvider.slots.requestInsertTemplate.emit({
|
||||
AIProvider.slots.requestInsertTemplate.next({
|
||||
template: TidyMindMapV3,
|
||||
mode: 'edgeless',
|
||||
});
|
||||
@@ -38,7 +38,7 @@ export const AIPreloadConfig = [
|
||||
icon: ImageIcon(),
|
||||
text: 'Add illustrations to the article',
|
||||
handler: () => {
|
||||
AIProvider.slots.requestInsertTemplate.emit({
|
||||
AIProvider.slots.requestInsertTemplate.next({
|
||||
template: redHat,
|
||||
mode: 'edgeless',
|
||||
});
|
||||
@@ -48,7 +48,7 @@ export const AIPreloadConfig = [
|
||||
icon: PenIcon(),
|
||||
text: 'Complete writing with AI',
|
||||
handler: () => {
|
||||
AIProvider.slots.requestInsertTemplate.emit({
|
||||
AIProvider.slots.requestInsertTemplate.next({
|
||||
template: completeWritingWithAI,
|
||||
mode: 'edgeless',
|
||||
});
|
||||
@@ -58,7 +58,7 @@ export const AIPreloadConfig = [
|
||||
icon: SendIcon(),
|
||||
text: 'Freely communicate with AI',
|
||||
handler: () => {
|
||||
AIProvider.slots.requestInsertTemplate.emit({
|
||||
AIProvider.slots.requestInsertTemplate.next({
|
||||
template: freelyCommunicateWithAI,
|
||||
mode: 'edgeless',
|
||||
});
|
||||
|
||||
@@ -75,7 +75,7 @@ export class AskAIToolbarButton extends WithDisposable(LitElement) {
|
||||
aiPanel.hide();
|
||||
extractSelectedContent(this.host)
|
||||
.then(context => {
|
||||
AIProvider.slots.requestSendWithChat.emit({
|
||||
AIProvider.slots.requestSendWithChat.next({
|
||||
input,
|
||||
context,
|
||||
host: this.host,
|
||||
|
||||
@@ -107,7 +107,7 @@ const othersGroup: AIItemGroupConfig = {
|
||||
showWhen: () => true,
|
||||
handler: host => {
|
||||
const panel = getAIPanelWidget(host);
|
||||
AIProvider.slots.requestOpenWithChat.emit({
|
||||
AIProvider.slots.requestOpenWithChat.next({
|
||||
host,
|
||||
mode: 'edgeless',
|
||||
autoSelect: true,
|
||||
|
||||
@@ -50,7 +50,7 @@ export function setupEdgelessElementToolbarAIEntry(
|
||||
aiPanel.hide();
|
||||
extractSelectedContent(edgeless.host)
|
||||
.then(context => {
|
||||
AIProvider.slots.requestSendWithChat.emit({
|
||||
AIProvider.slots.requestSendWithChat.next({
|
||||
input,
|
||||
context,
|
||||
host: edgeless.host,
|
||||
|
||||
@@ -13,7 +13,7 @@ class AICodeBlockWatcher extends LifeCycleWatcher {
|
||||
override mounted() {
|
||||
super.mounted();
|
||||
const { view } = this.std;
|
||||
view.viewUpdated.on(payload => {
|
||||
view.viewUpdated.subscribe(payload => {
|
||||
if (payload.type !== 'widget' || payload.method !== 'add') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ function getAIEdgelessRootWatcher(framework: FrameworkProvider) {
|
||||
override mounted() {
|
||||
super.mounted();
|
||||
const { view } = this.std;
|
||||
view.viewUpdated.on(payload => {
|
||||
view.viewUpdated.subscribe(payload => {
|
||||
if (payload.type !== 'widget' || payload.method !== 'add') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class AIImageBlockWatcher extends LifeCycleWatcher {
|
||||
override mounted() {
|
||||
super.mounted();
|
||||
const { view } = this.std;
|
||||
view.viewUpdated.on(payload => {
|
||||
view.viewUpdated.subscribe(payload => {
|
||||
if (payload.type !== 'widget' || payload.method !== 'add') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ function getAIPageRootWatcher(framework: FrameworkProvider) {
|
||||
override mounted() {
|
||||
super.mounted();
|
||||
const { view } = this.std;
|
||||
view.viewUpdated.on(payload => {
|
||||
view.viewUpdated.subscribe(payload => {
|
||||
if (payload.type !== 'widget' || payload.method !== 'add') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ const PaymentRequiredErrorRenderer = (host: EditorHost) => html`
|
||||
<ai-error-wrapper
|
||||
.text=${"You've reached the current usage cap for AFFiNE AI. You can subscribe to AFFiNE AI(with free 7-day-trial) to continue the AI experience!"}
|
||||
.actionText=${'Upgrade'}
|
||||
.onClick=${() => AIProvider.slots.requestUpgradePlan.emit({ host })}
|
||||
.onClick=${() => AIProvider.slots.requestUpgradePlan.next({ host })}
|
||||
></ai-error-wrapper>
|
||||
`;
|
||||
|
||||
@@ -196,7 +196,7 @@ const LoginRequiredErrorRenderer = (host: EditorHost) => html`
|
||||
<ai-error-wrapper
|
||||
.text=${'You need to login to AFFiNE Cloud to continue using AFFiNE AI.'}
|
||||
.actionText=${'Login'}
|
||||
.onClick=${() => AIProvider.slots.requestLogin.emit({ host })}
|
||||
.onClick=${() => AIProvider.slots.requestLogin.next({ host })}
|
||||
></ai-error-wrapper>
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { BlockService } from '@blocksuite/affine/block-std';
|
||||
import { Slot } from '@blocksuite/affine/global/slot';
|
||||
import { RootBlockSchema } from '@blocksuite/affine/model';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
export class MindmapService extends BlockService {
|
||||
static override readonly flavour = RootBlockSchema.model.flavour;
|
||||
|
||||
requestCenter = new Slot();
|
||||
// eslint-disable-next-line rxjs/finnish
|
||||
requestCenter = new Subject<void>();
|
||||
|
||||
center() {
|
||||
this.requestCenter.emit();
|
||||
this.requestCenter.next();
|
||||
}
|
||||
|
||||
override mounted(): void {}
|
||||
|
||||
@@ -65,7 +65,7 @@ export class MindmapSurfaceBlock extends BlockComponent<SurfaceBlockModel> {
|
||||
|
||||
private _setupCenterEffect() {
|
||||
this._disposables.add(
|
||||
this.mindmapService.requestCenter.on(() => {
|
||||
this.mindmapService.requestCenter.subscribe(() => {
|
||||
let bound: Bound;
|
||||
|
||||
this.model.elementModels.forEach(el => {
|
||||
@@ -85,7 +85,7 @@ export class MindmapSurfaceBlock extends BlockComponent<SurfaceBlockModel> {
|
||||
|
||||
private _setupRenderer() {
|
||||
this._disposables.add(
|
||||
this.model.elementUpdated.on(() => {
|
||||
this.model.elementUpdated.subscribe(() => {
|
||||
this.mindmapService.center();
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { EditorHost } from '@blocksuite/affine/block-std';
|
||||
import { Slot } from '@blocksuite/affine/global/slot';
|
||||
import { captureException } from '@sentry/react';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import type { ChatContextValue } from '../chat-panel/chat-context';
|
||||
import {
|
||||
@@ -123,22 +123,24 @@ export class AIProvider {
|
||||
private readonly slots = {
|
||||
// use case: when user selects "continue in chat" in an ask ai result panel
|
||||
// do we need to pass the context to the chat panel?
|
||||
requestOpenWithChat: new Slot<AIChatParams>(),
|
||||
requestSendWithChat: new Slot<AISendParams>(),
|
||||
requestInsertTemplate: new Slot<{
|
||||
/* eslint-disable rxjs/finnish */
|
||||
requestOpenWithChat: new Subject<AIChatParams>(),
|
||||
requestSendWithChat: new Subject<AISendParams>(),
|
||||
requestInsertTemplate: new Subject<{
|
||||
template: string;
|
||||
mode: 'page' | 'edgeless';
|
||||
}>(),
|
||||
requestLogin: new Slot<{ host: EditorHost }>(),
|
||||
requestUpgradePlan: new Slot<{ host: EditorHost }>(),
|
||||
requestLogin: new Subject<{ host: EditorHost }>(),
|
||||
requestUpgradePlan: new Subject<{ host: EditorHost }>(),
|
||||
// stream of AI actions triggered by users
|
||||
actions: new Slot<{
|
||||
actions: new Subject<{
|
||||
action: keyof BlockSuitePresets.AIActions;
|
||||
options: BlockSuitePresets.AITextActionOptions;
|
||||
event: ActionEventType;
|
||||
}>(),
|
||||
// downstream can emit this slot to notify ai presets that user info has been updated
|
||||
userInfo: new Slot<AIUserInfo | null>(),
|
||||
userInfo: new Subject<AIUserInfo | null>(),
|
||||
/* eslint-enable rxjs/finnish */
|
||||
};
|
||||
|
||||
// track the history of triggered actions (in memory only)
|
||||
@@ -162,7 +164,7 @@ export class AIProvider {
|
||||
) => {
|
||||
const options = args[0];
|
||||
const slots = this.slots;
|
||||
slots.actions.emit({
|
||||
slots.actions.next({
|
||||
action: id,
|
||||
options,
|
||||
event: 'started',
|
||||
@@ -186,31 +188,31 @@ export class AIProvider {
|
||||
try {
|
||||
user = await AIProvider.userInfo;
|
||||
yield* result;
|
||||
slots.actions.emit({
|
||||
slots.actions.next({
|
||||
action: id,
|
||||
options,
|
||||
event: 'finished',
|
||||
});
|
||||
} catch (err) {
|
||||
slots.actions.emit({
|
||||
slots.actions.next({
|
||||
action: id,
|
||||
options,
|
||||
event: 'error',
|
||||
});
|
||||
if (err instanceof PaymentRequiredError) {
|
||||
slots.actions.emit({
|
||||
slots.actions.next({
|
||||
action: id,
|
||||
options,
|
||||
event: 'aborted:paywall',
|
||||
});
|
||||
} else if (err instanceof UnauthorizedError) {
|
||||
slots.actions.emit({
|
||||
slots.actions.next({
|
||||
action: id,
|
||||
options,
|
||||
event: 'aborted:login-required',
|
||||
});
|
||||
} else {
|
||||
slots.actions.emit({
|
||||
slots.actions.next({
|
||||
action: id,
|
||||
options,
|
||||
event: 'aborted:server-error',
|
||||
@@ -232,7 +234,7 @@ export class AIProvider {
|
||||
return result
|
||||
.then(async result => {
|
||||
user = await AIProvider.userInfo;
|
||||
slots.actions.emit({
|
||||
slots.actions.next({
|
||||
action: id,
|
||||
options,
|
||||
event: 'finished',
|
||||
@@ -240,13 +242,13 @@ export class AIProvider {
|
||||
return result;
|
||||
})
|
||||
.catch(err => {
|
||||
slots.actions.emit({
|
||||
slots.actions.next({
|
||||
action: id,
|
||||
options,
|
||||
event: 'error',
|
||||
});
|
||||
if (err instanceof PaymentRequiredError) {
|
||||
slots.actions.emit({
|
||||
slots.actions.next({
|
||||
action: id,
|
||||
options,
|
||||
event: 'aborted:paywall',
|
||||
|
||||
@@ -527,13 +527,15 @@ Could you make a new website based on these notes and send back just the html fi
|
||||
return client.forkSession(options);
|
||||
});
|
||||
|
||||
const disposeRequestLoginHandler = AIProvider.slots.requestLogin.on(() => {
|
||||
globalDialogService.open('sign-in', {});
|
||||
});
|
||||
const disposeRequestLoginHandler = AIProvider.slots.requestLogin.subscribe(
|
||||
() => {
|
||||
globalDialogService.open('sign-in', {});
|
||||
}
|
||||
);
|
||||
|
||||
setupTracker();
|
||||
|
||||
return () => {
|
||||
disposeRequestLoginHandler.dispose();
|
||||
disposeRequestLoginHandler.unsubscribe();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { EditorHost } from '@blocksuite/affine/block-std';
|
||||
import type { GfxPrimitiveElementModel } from '@blocksuite/affine/block-std/gfx';
|
||||
import type { BlockModel } from '@blocksuite/affine/store';
|
||||
import { lowerCase, omit } from 'lodash-es';
|
||||
import type { Subject } from 'rxjs';
|
||||
|
||||
import { AIProvider } from './ai-provider';
|
||||
|
||||
@@ -60,9 +61,9 @@ type AIActionEventProperties = {
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
type BlocksuiteActionEvent = Parameters<
|
||||
Parameters<typeof AIProvider.slots.actions.on>[0]
|
||||
>[0];
|
||||
type SubjectValue<T> = T extends Subject<infer U> ? U : never;
|
||||
|
||||
type BlocksuiteActionEvent = SubjectValue<typeof AIProvider.slots.actions>;
|
||||
|
||||
const trackAction = ({
|
||||
eventName,
|
||||
@@ -252,15 +253,15 @@ const toTrackedOptions = (
|
||||
};
|
||||
|
||||
export function setupTracker() {
|
||||
AIProvider.slots.requestUpgradePlan.on(() => {
|
||||
AIProvider.slots.requestUpgradePlan.subscribe(() => {
|
||||
track.$.paywall.aiAction.viewPlans();
|
||||
});
|
||||
|
||||
AIProvider.slots.requestLogin.on(() => {
|
||||
AIProvider.slots.requestLogin.subscribe(() => {
|
||||
track.doc.editor.aiActions.requestSignIn();
|
||||
});
|
||||
|
||||
AIProvider.slots.actions.on(event => {
|
||||
AIProvider.slots.actions.subscribe(event => {
|
||||
const properties = toTrackedOptions(event);
|
||||
if (properties) {
|
||||
trackAction(properties);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Bound,
|
||||
getCommonBoundWithRotation,
|
||||
} from '@blocksuite/affine/global/gfx';
|
||||
import { Slot } from '@blocksuite/affine/global/slot';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import {
|
||||
AFFINE_AI_PANEL_WIDGET,
|
||||
@@ -22,7 +22,8 @@ export class CopilotTool extends BaseTool {
|
||||
|
||||
private _dragging = false;
|
||||
|
||||
draggingAreaUpdated = new Slot<boolean | void>();
|
||||
// eslint-disable-next-line rxjs/finnish
|
||||
draggingAreaUpdated = new Subject<boolean | void>();
|
||||
|
||||
dragLastPoint: [number, number] = [0, 0];
|
||||
|
||||
@@ -85,7 +86,7 @@ export class CopilotTool extends BaseTool {
|
||||
if (!this._dragging) return;
|
||||
|
||||
this._dragging = false;
|
||||
this.draggingAreaUpdated.emit(true);
|
||||
this.draggingAreaUpdated.next(true);
|
||||
}
|
||||
|
||||
override dragMove(e: PointerEventState): void {
|
||||
@@ -108,7 +109,7 @@ export class CopilotTool extends BaseTool {
|
||||
});
|
||||
}
|
||||
|
||||
this.draggingAreaUpdated.emit();
|
||||
this.draggingAreaUpdated.next();
|
||||
}
|
||||
|
||||
override dragStart(e: PointerEventState): void {
|
||||
@@ -116,7 +117,7 @@ export class CopilotTool extends BaseTool {
|
||||
|
||||
this._initDragState(e);
|
||||
this._dragging = true;
|
||||
this.draggingAreaUpdated.emit();
|
||||
this.draggingAreaUpdated.next();
|
||||
}
|
||||
|
||||
override mounted(): void {
|
||||
@@ -167,7 +168,7 @@ export class CopilotTool extends BaseTool {
|
||||
inoperable: true,
|
||||
});
|
||||
|
||||
this.draggingAreaUpdated.emit(true);
|
||||
this.draggingAreaUpdated.next(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ export function reportResponse(event: ActionEventType) {
|
||||
const lastAction = AIProvider.actionHistory.at(-1);
|
||||
if (!lastAction) return;
|
||||
|
||||
AIProvider.slots.actions.emit({
|
||||
AIProvider.slots.actions.next({
|
||||
action: lastAction.action,
|
||||
options: lastAction.options,
|
||||
event,
|
||||
|
||||
@@ -200,7 +200,7 @@ export class EdgelessCopilotWidget extends WidgetComponent<RootBlockModel> {
|
||||
const CopilotSelectionTool = this.gfx.tool.get('copilot');
|
||||
|
||||
this._disposables.add(
|
||||
CopilotSelectionTool.draggingAreaUpdated.on(shouldShowPanel => {
|
||||
CopilotSelectionTool.draggingAreaUpdated.subscribe(shouldShowPanel => {
|
||||
this._visible = true;
|
||||
this._updateSelection(CopilotSelectionTool.area);
|
||||
if (shouldShowPanel) {
|
||||
@@ -213,7 +213,7 @@ export class EdgelessCopilotWidget extends WidgetComponent<RootBlockModel> {
|
||||
);
|
||||
|
||||
this._disposables.add(
|
||||
this.gfx.viewport.viewportUpdated.on(() => {
|
||||
this.gfx.viewport.viewportUpdated.subscribe(() => {
|
||||
if (!this._visible) return;
|
||||
|
||||
this._updateSelection(CopilotSelectionTool.area);
|
||||
@@ -258,7 +258,7 @@ export class EdgelessCopilotWidget extends WidgetComponent<RootBlockModel> {
|
||||
|
||||
lockToolbar(disabled: boolean) {
|
||||
const legacySlot = this.std.get(EdgelessLegacySlotIdentifier);
|
||||
legacySlot.toolbarLocked.emit(disabled);
|
||||
legacySlot.toolbarLocked.next(disabled);
|
||||
}
|
||||
|
||||
override render() {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
customImageProxyMiddleware,
|
||||
ImageProxyService,
|
||||
} from '@blocksuite/affine/blocks/image';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/slot';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||
import type { DocMode } from '@blocksuite/affine/model';
|
||||
import { LinkPreviewerService } from '@blocksuite/affine/shared/services';
|
||||
import type { Store } from '@blocksuite/affine/store';
|
||||
@@ -49,13 +49,14 @@ const BlockSuiteEditorImpl = ({
|
||||
defaultOpenProperty,
|
||||
}: EditorProps) => {
|
||||
useEffect(() => {
|
||||
const disposable = page.slots.blockUpdated.once(() => {
|
||||
const disposable = page.slots.blockUpdated.subscribe(() => {
|
||||
disposable.unsubscribe();
|
||||
page.workspace.meta.setDocMeta(page.id, {
|
||||
updatedDate: Date.now(),
|
||||
});
|
||||
});
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
disposable.unsubscribe();
|
||||
};
|
||||
}, [page]);
|
||||
|
||||
@@ -154,15 +155,16 @@ export const BlockSuiteEditor = (props: EditorProps) => {
|
||||
return;
|
||||
}
|
||||
const timer = setTimeout(() => {
|
||||
disposable.dispose();
|
||||
disposable.unsubscribe();
|
||||
setError(new NoPageRootError(props.page));
|
||||
}, 20 * 1000);
|
||||
const disposable = props.page.slots.rootAdded.once(() => {
|
||||
const disposable = props.page.slots.rootAdded.subscribe(() => {
|
||||
disposable.unsubscribe();
|
||||
setIsLoading(false);
|
||||
clearTimeout(timer);
|
||||
});
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
disposable.unsubscribe();
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [props.page]);
|
||||
|
||||
@@ -104,8 +104,11 @@ const StarterBarNotEmpty = ({ doc }: { doc: Store }) => {
|
||||
|
||||
const { id, created } = rootComponent.focusFirstParagraph();
|
||||
if (created) {
|
||||
std.view.viewUpdated.once(v => {
|
||||
if (v.id === id) handleInlineAskAIAction(std.host, pageAIGroups);
|
||||
const subscription = std.view.viewUpdated.subscribe(v => {
|
||||
if (v.id === id) {
|
||||
subscription.unsubscribe();
|
||||
handleInlineAskAIAction(std.host, pageAIGroups);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
handleInlineAskAIAction(std.host, pageAIGroups);
|
||||
|
||||
@@ -47,7 +47,7 @@ export class EdgelessEditor extends SignalWatcher(
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._disposables.add(
|
||||
this.doc.slots.rootAdded.on(() => this.requestUpdate())
|
||||
this.doc.slots.rootAdded.subscribe(() => this.requestUpdate())
|
||||
);
|
||||
this.std = new BlockStdScope({
|
||||
store: this.doc,
|
||||
|
||||
@@ -59,7 +59,7 @@ export class PageEditor extends SignalWatcher(
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._disposables.add(
|
||||
this.doc.slots.rootAdded.on(() => this.requestUpdate())
|
||||
this.doc.slots.rootAdded.subscribe(() => this.requestUpdate())
|
||||
);
|
||||
this.std = new BlockStdScope({
|
||||
store: this.doc,
|
||||
|
||||
@@ -44,9 +44,7 @@ export function patchDocModeService(
|
||||
? docsService.list.primaryMode$(id)
|
||||
: docService.doc.primaryMode$;
|
||||
const sub = mode$.subscribe(m => handler((m || DEFAULT_MODE) as DocMode));
|
||||
return {
|
||||
dispose: sub.unsubscribe,
|
||||
};
|
||||
return sub;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export class EdgelessClipboardWatcher extends LifeCycleWatcher {
|
||||
override mounted() {
|
||||
super.mounted();
|
||||
const { view } = this.std;
|
||||
view.viewUpdated.on(payload => {
|
||||
view.viewUpdated.subscribe(payload => {
|
||||
if (payload.type !== 'block' || payload.method !== 'add') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import type {
|
||||
Container,
|
||||
ServiceIdentifier,
|
||||
} from '@blocksuite/affine/global/di';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/slot';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||
import {
|
||||
type ReferenceNodeConfig,
|
||||
ReferenceNodeConfigIdentifier,
|
||||
|
||||
@@ -57,7 +57,7 @@ const EdgelessNoteToggleButton = ({ note }: { note: NoteBlockModel }) => {
|
||||
|
||||
const { selection } = gfx;
|
||||
|
||||
const dispose = selection.slots.updated.on(() => {
|
||||
const dispose = selection.slots.updated.subscribe(() => {
|
||||
if (selection.has(note.id) && selection.editing) {
|
||||
note.doc.transact(() => {
|
||||
note.edgeless.collapse = false;
|
||||
@@ -65,7 +65,7 @@ const EdgelessNoteToggleButton = ({ note }: { note: NoteBlockModel }) => {
|
||||
}
|
||||
});
|
||||
|
||||
return () => dispose.dispose();
|
||||
return () => dispose.unsubscribe();
|
||||
}, [gfx, note]);
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
|
||||
@@ -11,11 +11,11 @@ export function useAllBlockSuiteDocMeta(docCollection: Workspace): DocMeta[] {
|
||||
weakMap.set(docCollection, baseAtom);
|
||||
baseAtom.onMount = set => {
|
||||
set([...docCollection.meta.docMetas]);
|
||||
const dispose = docCollection.slots.docListUpdated.on(() => {
|
||||
const dispose = docCollection.slots.docListUpdated.subscribe(() => {
|
||||
set([...docCollection.meta.docMetas]);
|
||||
});
|
||||
return () => {
|
||||
dispose.dispose();
|
||||
dispose.unsubscribe();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/slot';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||
import type { Store, Workspace } from '@blocksuite/affine/store';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
@@ -16,14 +16,14 @@ export function useDocCollectionPage(
|
||||
useEffect(() => {
|
||||
const group = new DisposableGroup();
|
||||
group.add(
|
||||
docCollection.slots.docCreated.on(id => {
|
||||
docCollection.slots.docCreated.subscribe(id => {
|
||||
if (pageId === id) {
|
||||
setPage(docCollection.getDoc(id));
|
||||
}
|
||||
})
|
||||
);
|
||||
group.add(
|
||||
docCollection.slots.docRemoved.on(id => {
|
||||
docCollection.slots.docRemoved.subscribe(id => {
|
||||
if (pageId === id) {
|
||||
setPage(null);
|
||||
}
|
||||
|
||||
@@ -69,16 +69,16 @@ export function useBlockSuitePagePreview(page: Store | null): Atom<string> {
|
||||
const baseAtom = atom<string>('');
|
||||
baseAtom.onMount = set => {
|
||||
const disposables = [
|
||||
page.slots.ready.on(() => {
|
||||
page.slots.ready.subscribe(() => {
|
||||
set(getPagePreviewText(page));
|
||||
}),
|
||||
page.slots.blockUpdated.on(() => {
|
||||
page.slots.blockUpdated.subscribe(() => {
|
||||
set(getPagePreviewText(page));
|
||||
}),
|
||||
];
|
||||
set(getPagePreviewText(page));
|
||||
return () => {
|
||||
disposables.forEach(disposable => disposable.dispose());
|
||||
disposables.forEach(disposable => disposable.unsubscribe());
|
||||
};
|
||||
};
|
||||
weakMap.set(page, baseAtom);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/slot';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||
import type { Store, Workspace } from '@blocksuite/affine/store';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
@@ -16,14 +16,14 @@ export function useDocCollectionPage(
|
||||
useEffect(() => {
|
||||
const group = new DisposableGroup();
|
||||
group.add(
|
||||
docCollection.slots.docCreated.on(id => {
|
||||
docCollection.slots.docCreated.subscribe(id => {
|
||||
if (pageId === id) {
|
||||
setPage(docCollection.getDoc(id));
|
||||
}
|
||||
})
|
||||
);
|
||||
group.add(
|
||||
docCollection.slots.docRemoved.on(id => {
|
||||
docCollection.slots.docRemoved.subscribe(id => {
|
||||
if (pageId === id) {
|
||||
setPage(null);
|
||||
}
|
||||
|
||||
@@ -111,14 +111,14 @@ export const WorkspaceSideEffects = () => {
|
||||
})
|
||||
);
|
||||
|
||||
const disposable = AIProvider.slots.requestInsertTemplate.on(
|
||||
const disposable = AIProvider.slots.requestInsertTemplate.subscribe(
|
||||
({ template, mode }) => {
|
||||
insertTemplate({ template, mode });
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
disposable.unsubscribe();
|
||||
insertTemplate.unsubscribe();
|
||||
};
|
||||
}, [
|
||||
@@ -134,14 +134,14 @@ export const WorkspaceSideEffects = () => {
|
||||
const globalDialogService = useService(GlobalDialogService);
|
||||
|
||||
useEffect(() => {
|
||||
const disposable = AIProvider.slots.requestUpgradePlan.on(() => {
|
||||
const disposable = AIProvider.slots.requestUpgradePlan.subscribe(() => {
|
||||
workspaceDialogService.open('setting', {
|
||||
activeTab: 'billing',
|
||||
});
|
||||
track.$.paywall.aiAction.viewPlans();
|
||||
});
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
disposable.unsubscribe();
|
||||
};
|
||||
}, [workspaceDialogService]);
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ export const EdgelessSnapshot = (props: Props) => {
|
||||
|
||||
// refresh viewport
|
||||
const gfx = editorHost.std.get(GfxControllerIdentifier);
|
||||
const disposable = editorHost.std.view.viewUpdated.on(payload => {
|
||||
const disposable = editorHost.std.view.viewUpdated.subscribe(payload => {
|
||||
if (
|
||||
payload.type !== 'block' ||
|
||||
payload.method !== 'add' ||
|
||||
@@ -110,7 +110,7 @@ export const EdgelessSnapshot = (props: Props) => {
|
||||
const bound = boundMap.get(docName);
|
||||
bound && gfx.viewport.setViewportByBound(bound);
|
||||
doc.readonly = true;
|
||||
disposable.dispose();
|
||||
disposable.unsubscribe();
|
||||
});
|
||||
|
||||
// append to dom node
|
||||
|
||||
@@ -42,13 +42,12 @@ export const ProfilePanel = () => {
|
||||
useEffect(() => {
|
||||
if (workspace?.docCollection) {
|
||||
setName(workspace.docCollection.meta.name ?? UNTITLED_WORKSPACE_NAME);
|
||||
const dispose = workspace.docCollection.meta.commonFieldsUpdated.on(
|
||||
() => {
|
||||
const dispose =
|
||||
workspace.docCollection.meta.commonFieldsUpdated.subscribe(() => {
|
||||
setName(workspace.docCollection.meta.name ?? UNTITLED_WORKSPACE_NAME);
|
||||
}
|
||||
);
|
||||
});
|
||||
return () => {
|
||||
dispose.dispose();
|
||||
dispose.unsubscribe();
|
||||
};
|
||||
} else {
|
||||
setName(UNTITLED_WORKSPACE_NAME);
|
||||
|
||||
@@ -18,10 +18,7 @@ import { ViewService } from '@affine/core/modules/workbench';
|
||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||
import { isNewTabTrigger } from '@affine/core/utils';
|
||||
import track from '@affine/track';
|
||||
import {
|
||||
type Disposable,
|
||||
DisposableGroup,
|
||||
} from '@blocksuite/affine/global/slot';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||
import { RefNodeSlotsProvider } from '@blocksuite/affine/rich-text';
|
||||
import {
|
||||
AiIcon,
|
||||
@@ -39,6 +36,7 @@ import {
|
||||
import clsx from 'clsx';
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import type { Subscription } from 'rxjs';
|
||||
|
||||
import { AffineErrorBoundary } from '../../../../components/affine/affine-error-boundary';
|
||||
import { GlobalPageHistoryModal } from '../../../../components/affine/page-history-modal';
|
||||
@@ -113,14 +111,18 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
}, [editorContainer, isActiveView, setActiveBlockSuiteEditor]);
|
||||
|
||||
useEffect(() => {
|
||||
const disposables: Disposable[] = [];
|
||||
const disposables: Subscription[] = [];
|
||||
const openHandler = () => {
|
||||
workbench.openSidebar();
|
||||
view.activeSidebarTab('chat');
|
||||
};
|
||||
disposables.push(AIProvider.slots.requestOpenWithChat.on(openHandler));
|
||||
disposables.push(AIProvider.slots.requestSendWithChat.on(openHandler));
|
||||
return () => disposables.forEach(d => d.dispose());
|
||||
disposables.push(
|
||||
AIProvider.slots.requestOpenWithChat.subscribe(openHandler)
|
||||
);
|
||||
disposables.push(
|
||||
AIProvider.slots.requestSendWithChat.subscribe(openHandler)
|
||||
);
|
||||
return () => disposables.forEach(d => d.unsubscribe());
|
||||
}, [activeSidebarTab, view, workbench]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -169,7 +171,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
if (refNodeSlots) {
|
||||
disposable.add(
|
||||
// the event should not be emitted by AffineReference
|
||||
refNodeSlots.docLinkClicked.on(
|
||||
refNodeSlots.docLinkClicked.subscribe(
|
||||
({ pageId, params, openMode, event, host }) => {
|
||||
if (host !== editorContainer.host) {
|
||||
return;
|
||||
|
||||
@@ -109,7 +109,7 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
|
||||
const docModeService = editor.host.std.get(DocModeProvider);
|
||||
const refNodeService = editor.host.std.getOptional(RefNodeSlotsProvider);
|
||||
const disposable = [
|
||||
refNodeService?.docLinkClicked.on(({ host }) => {
|
||||
refNodeService?.docLinkClicked.subscribe(({ host }) => {
|
||||
if (host === editor.host) {
|
||||
(chatPanelRef.current as ChatPanel).doc = editor.doc;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
|
||||
}, editor.doc.id),
|
||||
];
|
||||
|
||||
return () => disposable.forEach(d => d?.dispose());
|
||||
return () => disposable.forEach(d => d?.unsubscribe());
|
||||
}, [editor, framework]);
|
||||
|
||||
return <div className={styles.root} ref={containerRef} />;
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
WorkspacesService,
|
||||
} from '@affine/core/modules/workspace';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/slot';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||
import { type DocMode, DocModes } from '@blocksuite/affine/model';
|
||||
import { RefNodeSlotsProvider } from '@blocksuite/affine/rich-text';
|
||||
import { Logo1Icon } from '@blocksuite/icons/rc';
|
||||
@@ -201,7 +201,7 @@ const SharePageInner = ({
|
||||
editorContainer.host?.std.getOptional(RefNodeSlotsProvider);
|
||||
if (refNodeSlots) {
|
||||
disposable.add(
|
||||
refNodeSlots.docLinkClicked.on(({ pageId, params }) => {
|
||||
refNodeSlots.docLinkClicked.subscribe(({ pageId, params }) => {
|
||||
if (params) {
|
||||
const { mode, blockIds, elementIds } = params;
|
||||
jumpToPageBlock(workspaceId, pageId, mode, blockIds, elementIds);
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
customImageProxyMiddleware,
|
||||
ImageProxyService,
|
||||
} from '@blocksuite/affine/blocks/image';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/slot';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||
import { RefNodeSlotsProvider } from '@blocksuite/affine/rich-text';
|
||||
import { LinkPreviewerService } from '@blocksuite/affine/shared/services';
|
||||
import {
|
||||
@@ -162,7 +162,7 @@ const DetailPageImpl = () => {
|
||||
const disposable = new DisposableGroup();
|
||||
if (refNodeService) {
|
||||
disposable.add(
|
||||
refNodeService.docLinkClicked.on(({ pageId, params }) => {
|
||||
refNodeService.docLinkClicked.subscribe(({ pageId, params }) => {
|
||||
if (params) {
|
||||
const { mode, blockIds, elementIds } = params;
|
||||
return jumpToPageBlock(
|
||||
|
||||
@@ -52,7 +52,7 @@ export class AuthService extends Service {
|
||||
skip(1) // skip the initial value
|
||||
)
|
||||
.subscribe(({ account }) => {
|
||||
AIProvider.slots.userInfo.emit(toAIUserInfo(account));
|
||||
AIProvider.slots.userInfo.next(toAIUserInfo(account));
|
||||
|
||||
if (account === null) {
|
||||
this.eventBus.emit(AccountLoggedOut, account);
|
||||
|
||||
@@ -17,7 +17,7 @@ export class EditorSettingService extends Service {
|
||||
onWorkspaceInitialized(workspace: Workspace) {
|
||||
// set default mode for new doc
|
||||
|
||||
workspace.docCollection.slots.docCreated.on(docId => {
|
||||
workspace.docCollection.slots.docCreated.subscribe(docId => {
|
||||
const preferMode = this.editorSetting.settings$.value.newDocDefaultMode;
|
||||
const docsService = workspace.scope.get(DocsService);
|
||||
const mode = preferMode === 'ask' ? 'page' : preferMode;
|
||||
|
||||
@@ -261,7 +261,9 @@ export class Editor extends Entity {
|
||||
scrollViewport?.removeEventListener('scroll', saveScrollPosition);
|
||||
});
|
||||
if (gfx) {
|
||||
unsubs.push(gfx.viewport.viewportUpdated.on(saveScrollPosition).dispose);
|
||||
const subscription =
|
||||
gfx.viewport.viewportUpdated.subscribe(saveScrollPosition);
|
||||
unsubs.push(subscription.unsubscribe.bind(subscription));
|
||||
}
|
||||
|
||||
// update selection when focusAt$ changed
|
||||
|
||||
@@ -9,11 +9,8 @@ import { EditorService } from '@affine/core/modules/editor';
|
||||
import { GuardService } from '@affine/core/modules/permissions';
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||
import { Bound } from '@blocksuite/affine/global/gfx';
|
||||
import {
|
||||
type Disposable,
|
||||
DisposableGroup,
|
||||
} from '@blocksuite/affine/global/slot';
|
||||
import { RefNodeSlotsProvider } from '@blocksuite/affine/rich-text';
|
||||
import {
|
||||
FrameworkScope,
|
||||
@@ -23,6 +20,7 @@ import {
|
||||
} from '@toeverything/infra';
|
||||
import clsx from 'clsx';
|
||||
import { lazy, Suspense, useCallback, useEffect } from 'react';
|
||||
import type { Subscription } from 'rxjs';
|
||||
|
||||
import { WorkbenchService } from '../../../workbench';
|
||||
import type { DocReferenceInfo } from '../../entities/peek-view';
|
||||
@@ -101,7 +99,7 @@ function DocPeekPreviewEditor({
|
||||
// doc change event inside peek view should be handled by peek view
|
||||
disposableGroup.add(
|
||||
// todo(@pengx17): seems not working
|
||||
refNodeSlots.docLinkClicked.on(options => {
|
||||
refNodeSlots.docLinkClicked.subscribe(options => {
|
||||
if (options.host !== editorContainer.host) {
|
||||
return;
|
||||
}
|
||||
@@ -129,7 +127,7 @@ function DocPeekPreviewEditor({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const disposables: Disposable[] = [];
|
||||
const disposables: Subscription[] = [];
|
||||
const openHandler = () => {
|
||||
if (doc) {
|
||||
workbench.openDoc(doc.id);
|
||||
@@ -137,9 +135,13 @@ function DocPeekPreviewEditor({
|
||||
// chat panel open is already handled in <DetailPageImpl />
|
||||
}
|
||||
};
|
||||
disposables.push(AIProvider.slots.requestOpenWithChat.on(openHandler));
|
||||
disposables.push(AIProvider.slots.requestSendWithChat.on(openHandler));
|
||||
return () => disposables.forEach(d => d.dispose());
|
||||
disposables.push(
|
||||
AIProvider.slots.requestOpenWithChat.subscribe(openHandler)
|
||||
);
|
||||
disposables.push(
|
||||
AIProvider.slots.requestSendWithChat.subscribe(openHandler)
|
||||
);
|
||||
return () => disposables.forEach(d => d.unsubscribe());
|
||||
}, [doc, peekView, workbench, workspace.id]);
|
||||
|
||||
const openOutlinePanel = useCallback(() => {
|
||||
|
||||
@@ -23,8 +23,10 @@ export class TagStore extends Store {
|
||||
|
||||
subscribe(cb: () => void) {
|
||||
const disposable =
|
||||
this.workspaceService.workspace.docCollection.slots.docListUpdated.on(cb);
|
||||
return disposable.dispose;
|
||||
this.workspaceService.workspace.docCollection.slots.docListUpdated.subscribe(
|
||||
cb
|
||||
);
|
||||
return disposable.unsubscribe.bind(disposable);
|
||||
}
|
||||
|
||||
constructor(private readonly workspaceService: WorkspaceService) {
|
||||
|
||||
@@ -73,9 +73,11 @@ export class Workspace extends Entity {
|
||||
name$ = LiveData.from<string | undefined>(
|
||||
new Observable(subscriber => {
|
||||
subscriber.next(this.docCollection.meta.name);
|
||||
return this.docCollection.meta.commonFieldsUpdated.on(() => {
|
||||
subscriber.next(this.docCollection.meta.name);
|
||||
}).dispose;
|
||||
const subscription =
|
||||
this.docCollection.meta.commonFieldsUpdated.subscribe(() => {
|
||||
subscriber.next(this.docCollection.meta.name);
|
||||
});
|
||||
return subscription.unsubscribe.bind(subscription);
|
||||
}),
|
||||
undefined
|
||||
);
|
||||
@@ -83,9 +85,11 @@ export class Workspace extends Entity {
|
||||
avatar$ = LiveData.from<string | undefined>(
|
||||
new Observable(subscriber => {
|
||||
subscriber.next(this.docCollection.meta.avatar);
|
||||
return this.docCollection.meta.commonFieldsUpdated.on(() => {
|
||||
subscriber.next(this.docCollection.meta.avatar);
|
||||
}).dispose;
|
||||
const subscription =
|
||||
this.docCollection.meta.commonFieldsUpdated.subscribe(() => {
|
||||
subscriber.next(this.docCollection.meta.avatar);
|
||||
});
|
||||
return subscription.unsubscribe.bind(subscription);
|
||||
}),
|
||||
undefined
|
||||
);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Slot } from '@blocksuite/affine/global/slot';
|
||||
import { SpecProvider } from '@blocksuite/affine/shared/utils';
|
||||
import {
|
||||
AwarenessStore,
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
type YBlock,
|
||||
} from '@blocksuite/affine/store';
|
||||
import { signal } from '@preact/signals-core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Awareness } from 'y-protocols/awareness.js';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
@@ -37,7 +37,7 @@ export class DocImpl implements Doc {
|
||||
|
||||
private readonly _historyObserver = () => {
|
||||
this._updateCanUndoRedoSignals();
|
||||
this.slots.historyUpdated.emit();
|
||||
this.slots.historyUpdated.next();
|
||||
};
|
||||
|
||||
private readonly _initSubDoc = () => {
|
||||
@@ -65,7 +65,8 @@ export class DocImpl implements Doc {
|
||||
|
||||
private _loaded!: boolean;
|
||||
|
||||
private readonly _onLoadSlot = new Slot();
|
||||
// eslint-disable-next-line rxjs/finnish
|
||||
private readonly _onLoadSlot = new Subject();
|
||||
|
||||
/** Indicate whether the block tree is ready */
|
||||
private _ready = false;
|
||||
@@ -98,8 +99,10 @@ export class DocImpl implements Doc {
|
||||
readonly rootDoc: Y.Doc;
|
||||
|
||||
readonly slots = {
|
||||
historyUpdated: new Slot(),
|
||||
yBlockUpdated: new Slot<
|
||||
// eslint-disable-next-line rxjs/finnish
|
||||
historyUpdated: new Subject<void>(),
|
||||
// eslint-disable-next-line rxjs/finnish
|
||||
yBlockUpdated: new Subject<
|
||||
| {
|
||||
type: 'add';
|
||||
id: string;
|
||||
@@ -170,11 +173,11 @@ export class DocImpl implements Doc {
|
||||
}
|
||||
|
||||
private _handleYBlockAdd(id: string) {
|
||||
this.slots.yBlockUpdated.emit({ type: 'add', id });
|
||||
this.slots.yBlockUpdated.next({ type: 'add', id });
|
||||
}
|
||||
|
||||
private _handleYBlockDelete(id: string) {
|
||||
this.slots.yBlockUpdated.emit({ type: 'delete', id });
|
||||
this.slots.yBlockUpdated.next({ type: 'delete', id });
|
||||
}
|
||||
|
||||
private _handleYEvent(event: Y.YEvent<YBlock | Y.Text | Y.Array<unknown>>) {
|
||||
@@ -229,13 +232,13 @@ export class DocImpl implements Doc {
|
||||
private _destroy() {
|
||||
this.awarenessStore.destroy();
|
||||
this._ySpaceDoc.destroy();
|
||||
this._onLoadSlot.dispose();
|
||||
this._onLoadSlot.unsubscribe();
|
||||
this._loaded = false;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._destroy();
|
||||
this.slots.historyUpdated.dispose();
|
||||
this.slots.historyUpdated.unsubscribe();
|
||||
|
||||
if (this.ready) {
|
||||
this._yBlocks.unobserveDeep(this._handleYEvents);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Slot } from '@blocksuite/affine/global/slot';
|
||||
import {
|
||||
createYProxy,
|
||||
type DocMeta,
|
||||
type DocsPropertiesMeta,
|
||||
type WorkspaceMeta,
|
||||
} from '@blocksuite/affine/store';
|
||||
import { Subject } from 'rxjs';
|
||||
import type * as Y from 'yjs';
|
||||
|
||||
type MetaState = {
|
||||
@@ -15,10 +15,12 @@ type MetaState = {
|
||||
};
|
||||
|
||||
export class WorkspaceMetaImpl implements WorkspaceMeta {
|
||||
commonFieldsUpdated = new Slot();
|
||||
docMetaAdded = new Slot<string>();
|
||||
docMetaRemoved = new Slot<string>();
|
||||
docMetaUpdated = new Slot();
|
||||
/* eslint-disable rxjs/finnish */
|
||||
commonFieldsUpdated = new Subject<void>();
|
||||
docMetaAdded = new Subject<string>();
|
||||
docMetaRemoved = new Subject<string>();
|
||||
docMetaUpdated = new Subject<void>();
|
||||
/* eslint-enable rxjs/finnish */
|
||||
|
||||
private readonly _handleDocCollectionMetaEvents = (
|
||||
events: Y.YEvent<Y.Array<unknown> | Y.Text | Y.Map<unknown>>[]
|
||||
@@ -81,7 +83,7 @@ export class WorkspaceMetaImpl implements WorkspaceMeta {
|
||||
|
||||
setProperties(meta: DocsPropertiesMeta) {
|
||||
this._proxy.properties = meta;
|
||||
this.docMetaUpdated.emit();
|
||||
this.docMetaUpdated.next();
|
||||
}
|
||||
|
||||
get docMetas() {
|
||||
@@ -108,7 +110,7 @@ export class WorkspaceMetaImpl implements WorkspaceMeta {
|
||||
}
|
||||
|
||||
private _handleCommonFieldsEvent() {
|
||||
this.commonFieldsUpdated.emit();
|
||||
this.commonFieldsUpdated.next();
|
||||
}
|
||||
|
||||
private _handleDocMetaEvent() {
|
||||
@@ -118,7 +120,7 @@ export class WorkspaceMetaImpl implements WorkspaceMeta {
|
||||
|
||||
docMetas.forEach(docMeta => {
|
||||
if (!_prevDocs.has(docMeta.id)) {
|
||||
this.docMetaAdded.emit(docMeta.id);
|
||||
this.docMetaAdded.next(docMeta.id);
|
||||
}
|
||||
newDocs.add(docMeta.id);
|
||||
});
|
||||
@@ -126,13 +128,13 @@ export class WorkspaceMetaImpl implements WorkspaceMeta {
|
||||
_prevDocs.forEach(prevDocId => {
|
||||
const isRemoved = newDocs.has(prevDocId) === false;
|
||||
if (isRemoved) {
|
||||
this.docMetaRemoved.emit(prevDocId);
|
||||
this.docMetaRemoved.next(prevDocId);
|
||||
}
|
||||
});
|
||||
|
||||
this._prevDocs = newDocs;
|
||||
|
||||
this.docMetaUpdated.emit();
|
||||
this.docMetaUpdated.next();
|
||||
}
|
||||
|
||||
addDocMeta(doc: DocMeta, index?: number) {
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
BlockSuiteError,
|
||||
ErrorCode,
|
||||
} from '@blocksuite/affine/global/exceptions';
|
||||
import { Slot } from '@blocksuite/affine/global/slot';
|
||||
import { NoopLogger } from '@blocksuite/affine/global/utils';
|
||||
import {
|
||||
type CreateBlocksOptions,
|
||||
@@ -19,6 +18,7 @@ import {
|
||||
type BlobSource,
|
||||
MemoryBlobSource,
|
||||
} from '@blocksuite/affine/sync';
|
||||
import { Subject } from 'rxjs';
|
||||
import type { Awareness } from 'y-protocols/awareness.js';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
@@ -46,9 +46,11 @@ export class WorkspaceImpl implements Workspace {
|
||||
meta: WorkspaceMeta;
|
||||
|
||||
slots = {
|
||||
docListUpdated: new Slot(),
|
||||
docRemoved: new Slot<string>(),
|
||||
docCreated: new Slot<string>(),
|
||||
/* eslint-disable rxjs/finnish */
|
||||
docListUpdated: new Subject<void>(),
|
||||
docRemoved: new Subject<string>(),
|
||||
docCreated: new Subject<string>(),
|
||||
/* eslint-enable rxjs/finnish */
|
||||
};
|
||||
|
||||
get docs() {
|
||||
@@ -82,7 +84,7 @@ export class WorkspaceImpl implements Workspace {
|
||||
}
|
||||
|
||||
private _bindDocMetaEvents() {
|
||||
this.meta.docMetaAdded.on(docId => {
|
||||
this.meta.docMetaAdded.subscribe(docId => {
|
||||
const doc = new DocImpl({
|
||||
id: docId,
|
||||
collection: this,
|
||||
@@ -91,14 +93,14 @@ export class WorkspaceImpl implements Workspace {
|
||||
this.blockCollections.set(doc.id, doc);
|
||||
});
|
||||
|
||||
this.meta.docMetaUpdated.on(() => this.slots.docListUpdated.emit());
|
||||
this.meta.docMetaUpdated.subscribe(() => this.slots.docListUpdated.next());
|
||||
|
||||
this.meta.docMetaRemoved.on(id => {
|
||||
this.meta.docMetaRemoved.subscribe(id => {
|
||||
const doc = this._getDoc(id);
|
||||
if (!doc) return;
|
||||
this.blockCollections.delete(id);
|
||||
doc.remove();
|
||||
this.slots.docRemoved.emit(id);
|
||||
this.slots.docRemoved.next(id);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -126,7 +128,7 @@ export class WorkspaceImpl implements Workspace {
|
||||
createDate: Date.now(),
|
||||
tags: [],
|
||||
});
|
||||
this.slots.docCreated.emit(docId);
|
||||
this.slots.docCreated.next(docId);
|
||||
return this.getDoc(docId, {
|
||||
id: docId,
|
||||
query,
|
||||
|
||||
Reference in New Issue
Block a user