refactor(editor): rx state management in turbo renderer (#11200)

# Refactor Turbo Renderer State Management to Use RxJS

### TL;DR

Refactored the TurboRenderer state management to use RxJS observables instead of direct state mutations, improving state transitions and reactivity.

### What changed?

- Replaced the public `state` property with a private `state$` BehaviorSubject in `ViewportTurboRendererExtension`
- Added proper state transition logging using RxJS operators
- Combined multiple event subscriptions using `merge` operator for better organization
- Improved state transition logic in the `refresh()` method
- Updated the `zooming$` and `panning$` signals in the Viewport

### Why make this change?

This refactoring improves the codebase by:
1. Using a more consistent reactive programming model with RxJS
2. Making state transitions more explicit and traceable
3. Reducing potential bugs from manual state management
4. Improving code organization by combining related event streams
5. Ensuring proper cleanup of resources when components are disposed

The change maintains the same functionality while making the code more maintainable and the state management more robust.
This commit is contained in:
doodlewind
2025-03-27 04:50:32 +00:00
parent 22ef32f5c2
commit bb1bbccd0f
4 changed files with 88 additions and 69 deletions

View File

@@ -5,9 +5,8 @@ import {
type IVec,
Vec,
} from '@blocksuite/global/gfx';
import { signal } from '@preact/signals-core';
import debounce from 'lodash-es/debounce';
import { debounceTime, Subject } from 'rxjs';
import { BehaviorSubject, debounceTime, Subject } from 'rxjs';
import type { GfxViewportElement } from '.';
@@ -100,19 +99,19 @@ export class Viewport {
center: IVec;
}>();
zooming$ = signal(false);
panning$ = signal(false);
zooming$ = new BehaviorSubject<boolean>(false);
panning$ = new BehaviorSubject<boolean>(false);
ZOOM_MAX = ZOOM_MAX;
ZOOM_MIN = ZOOM_MIN;
private readonly _resetZooming = debounce(() => {
this.zooming$.value = false;
this.zooming$.next(false);
}, 200);
private readonly _resetPanning = debounce(() => {
this.panning$.value = false;
this.panning$.next(false);
}, 200);
constructor() {
@@ -296,9 +295,8 @@ export class Viewport {
this.viewportMoved.complete();
this.viewportUpdated.complete();
this._resizeSubject.complete();
this.zooming$.value = false;
this.panning$.value = false;
this.zooming$.complete();
this.panning$.complete();
}
getFitToScreenData(
@@ -371,7 +369,7 @@ export class Viewport {
this._center.x = centerX;
this._center.y = centerY;
this.panning$.value = true;
this.panning$.next(true);
this.viewportUpdated.next({
zoom: this.zoom,
center: Vec.toVec(this.center) as IVec,
@@ -530,7 +528,7 @@ export class Viewport {
Vec.mul(offset, prevZoom / newZoom)
);
if (wheel) {
this.zooming$.value = true;
this.zooming$.next(true);
}
this.setCenter(newCenter[0], newCenter[1], forceUpdate);
this.viewportUpdated.next({