feat(ui): convert all my pubsubs to atoms

its the same but better
This commit is contained in:
psychedelicious 2024-08-08 18:45:06 +10:00
parent 7f6d439fd1
commit c90d3f3bb9
5 changed files with 31 additions and 83 deletions

View File

@ -19,7 +19,7 @@ export const TransformToolButton = memo(() => {
if (!canvasManager) { if (!canvasManager) {
return; return;
} }
return canvasManager.transformingEntity.subscribe((newValue) => { return canvasManager.$transformingEntity.listen((newValue) => {
setIsTransforming(Boolean(newValue)); setIsTransforming(Boolean(newValue));
}); });
}, [canvasManager]); }, [canvasManager]);

View File

@ -2,7 +2,6 @@ import type { Store } from '@reduxjs/toolkit';
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import type { RootState } from 'app/store/store'; import type { RootState } from 'app/store/store';
import type { JSONObject } from 'common/types'; import type { JSONObject } from 'common/types';
import { PubSub } from 'common/util/PubSub/PubSub';
import type { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine'; import type { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine';
import type { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine'; import type { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
import type { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage'; import type { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage';
@ -36,14 +35,11 @@ import { RGBA_RED } from 'features/controlLayers/store/types';
import { isValidLayer } from 'features/nodes/util/graph/generation/addLayers'; import { isValidLayer } from 'features/nodes/util/graph/generation/addLayers';
import type Konva from 'konva'; import type Konva from 'konva';
import { clamp } from 'lodash-es'; import { clamp } from 'lodash-es';
import type { WritableAtom } from 'nanostores';
import { atom } from 'nanostores'; import { atom } from 'nanostores';
import type { Logger } from 'roarr'; import type { Logger } from 'roarr';
import { import { getImageDTO, uploadImage } from 'services/api/endpoints/images';
getImageDTO as defaultGetImageDTO, import type { ImageDTO } from 'services/api/types';
getImageDTO,
uploadImage as defaultUploadImage,
} from 'services/api/endpoints/images';
import type { ImageCategory, ImageDTO } from 'services/api/types';
import { assert } from 'tsafe'; import { assert } from 'tsafe';
import { CanvasBackground } from './CanvasBackground'; import { CanvasBackground } from './CanvasBackground';
@ -55,34 +51,6 @@ import { CanvasStagingArea } from './CanvasStagingArea';
import { CanvasStateApi } from './CanvasStateApi'; import { CanvasStateApi } from './CanvasStateApi';
import { setStageEventHandlers } from './events'; import { setStageEventHandlers } from './events';
// type Extents = {
// minX: number;
// minY: number;
// maxX: number;
// maxY: number;
// };
// type GetBboxTask = {
// id: string;
// type: 'get_bbox';
// data: { imageData: ImageData };
// };
// type GetBboxResult = {
// id: string;
// type: 'get_bbox';
// data: { extents: Extents | null };
// };
type Util = {
getImageDTO: (imageName: string) => Promise<ImageDTO | null>;
uploadImage: (
blob: Blob,
fileName: string,
image_category: ImageCategory,
is_intermediate: boolean
) => Promise<ImageDTO>;
};
type EntityStateAndAdapter = type EntityStateAndAdapter =
| { | {
id: string; id: string;
@ -118,7 +86,6 @@ export class CanvasManager {
layers: Map<string, CanvasLayerAdapter>; layers: Map<string, CanvasLayerAdapter>;
regions: Map<string, CanvasMaskAdapter>; regions: Map<string, CanvasMaskAdapter>;
inpaintMask: CanvasMaskAdapter; inpaintMask: CanvasMaskAdapter;
util: Util;
stateApi: CanvasStateApi; stateApi: CanvasStateApi;
preview: CanvasPreview; preview: CanvasPreview;
background: CanvasBackground; background: CanvasBackground;
@ -126,8 +93,6 @@ export class CanvasManager {
log: Logger; log: Logger;
workerLog: Logger; workerLog: Logger;
transformingEntity: PubSub<CanvasEntityIdentifier | null>;
_store: Store<RootState>; _store: Store<RootState>;
_prevState: CanvasV2State; _prevState: CanvasV2State;
_isFirstRender: boolean = true; _isFirstRender: boolean = true;
@ -136,26 +101,18 @@ export class CanvasManager {
_worker: Worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module', name: 'worker' }); _worker: Worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module', name: 'worker' });
_tasks: Map<string, { task: GetBboxTask; onComplete: (extents: Extents | null) => void }> = new Map(); _tasks: Map<string, { task: GetBboxTask; onComplete: (extents: Extents | null) => void }> = new Map();
toolState: PubSub<CanvasV2State['tool']>; $transformingEntity: WritableAtom<CanvasEntityIdentifier | null> = atom();
currentFill: PubSub<RgbaColor>; $toolState: WritableAtom<CanvasV2State['tool']> = atom();
selectedEntity: PubSub<EntityStateAndAdapter | null>; $currentFill: WritableAtom<RgbaColor> = atom();
selectedEntityIdentifier: PubSub<CanvasEntityIdentifier | null>; $selectedEntity: WritableAtom<EntityStateAndAdapter | null> = atom();
$selectedEntityIdentifier: WritableAtom<CanvasEntityIdentifier | null> = atom();
constructor( constructor(stage: Konva.Stage, container: HTMLDivElement, store: Store<RootState>) {
stage: Konva.Stage,
container: HTMLDivElement,
store: Store<RootState>,
getImageDTO: Util['getImageDTO'] = defaultGetImageDTO,
uploadImage: Util['uploadImage'] = defaultUploadImage
) {
this.stage = stage; this.stage = stage;
this.container = container; this.container = container;
this._store = store; this._store = store;
this.stateApi = new CanvasStateApi(this._store, this); this.stateApi = new CanvasStateApi(this._store, this);
this.transformingEntity = new PubSub<CanvasEntityIdentifier | null>(null);
this.toolState = new PubSub(this.stateApi.getToolState());
this._prevState = this.stateApi.getState(); this._prevState = this.stateApi.getState();
this.log = logger('canvas').child((message) => { this.log = logger('canvas').child((message) => {
@ -169,11 +126,6 @@ export class CanvasManager {
}); });
this.workerLog = logger('worker'); this.workerLog = logger('worker');
this.util = {
getImageDTO,
uploadImage,
};
this.preview = new CanvasPreview(this); this.preview = new CanvasPreview(this);
this.stage.add(this.preview.getLayer()); this.stage.add(this.preview.getLayer());
@ -208,15 +160,11 @@ export class CanvasManager {
this.log.error('Worker message error'); this.log.error('Worker message error');
}; };
this.currentFill = new PubSub(this.getCurrentFill()); this.$transformingEntity.set(null);
this.selectedEntityIdentifier = new PubSub( this.$toolState.set(this.stateApi.getToolState());
this.stateApi.getState().selectedEntityIdentifier, this.$selectedEntityIdentifier.set(this.stateApi.getState().selectedEntityIdentifier);
(a, b) => a?.id === b?.id this.$currentFill.set(this.getCurrentFill());
); this.$selectedEntity.set(this.getSelectedEntity());
this.selectedEntity = new PubSub(
this.getSelectedEntity(),
(a, b) => a?.state === b?.state && a?.adapter === b?.adapter
);
this.inpaintMask = new CanvasMaskAdapter(this.stateApi.getInpaintMaskState(), this); this.inpaintMask = new CanvasMaskAdapter(this.stateApi.getInpaintMaskState(), this);
this.stage.add(this.inpaintMask.konva.layer); this.stage.add(this.inpaintMask.konva.layer);
@ -395,7 +343,7 @@ export class CanvasManager {
}; };
getTransformingLayer() { getTransformingLayer() {
const transformingEntity = this.transformingEntity.getValue(); const transformingEntity = this.$transformingEntity.get();
if (!transformingEntity) { if (!transformingEntity) {
return null; return null;
} }
@ -414,7 +362,7 @@ export class CanvasManager {
} }
getIsTransforming() { getIsTransforming() {
return Boolean(this.transformingEntity.getValue()); return Boolean(this.$transformingEntity.get());
} }
startTransform() { startTransform() {
@ -428,7 +376,7 @@ export class CanvasManager {
} }
// TODO(psyche): Support other entity types // TODO(psyche): Support other entity types
entity.adapter.transformer.startTransform(); entity.adapter.transformer.startTransform();
this.transformingEntity.publish({ id: entity.id, type: entity.type }); this.$transformingEntity.set({ id: entity.id, type: entity.type });
} }
async applyTransform() { async applyTransform() {
@ -436,7 +384,7 @@ export class CanvasManager {
if (layer) { if (layer) {
await layer.transformer.applyTransform(); await layer.transformer.applyTransform();
} }
this.transformingEntity.publish(null); this.$transformingEntity.set(null);
} }
cancelTransform() { cancelTransform() {
@ -444,7 +392,7 @@ export class CanvasManager {
if (layer) { if (layer) {
layer.transformer.stopTransform(); layer.transformer.stopTransform();
} }
this.transformingEntity.publish(null); this.$transformingEntity.set(null);
} }
render = async () => { render = async () => {
@ -537,10 +485,10 @@ export class CanvasManager {
await this.renderControlAdapters(); await this.renderControlAdapters();
} }
this.toolState.publish(state.tool); this.$toolState.set(state.tool);
this.selectedEntityIdentifier.publish(state.selectedEntityIdentifier); this.$selectedEntityIdentifier.set(state.selectedEntityIdentifier);
this.selectedEntity.publish(this.getSelectedEntity()); this.$selectedEntity.set(this.getSelectedEntity());
this.currentFill.publish(this.getCurrentFill()); this.$currentFill.set(this.getCurrentFill());
if ( if (
this._isFirstRender || this._isFirstRender ||
@ -740,7 +688,7 @@ export class CanvasManager {
getCompositeLayerImageDTO = async (rect?: Rect): Promise<ImageDTO> => { getCompositeLayerImageDTO = async (rect?: Rect): Promise<ImageDTO> => {
const blob = await this.getCompositeLayerBlob(rect); const blob = await this.getCompositeLayerBlob(rect);
const imageDTO = await this.util.uploadImage(blob, 'composite-layer.png', 'general', true); const imageDTO = await uploadImage(blob, 'composite-layer.png', 'general', true);
this.stateApi.setLayerImageCache(imageDTO); this.stateApi.setLayerImageCache(imageDTO);
return imageDTO; return imageDTO;
}; };
@ -755,7 +703,7 @@ export class CanvasManager {
getInpaintMaskImageDTO = async (rect?: Rect): Promise<ImageDTO> => { getInpaintMaskImageDTO = async (rect?: Rect): Promise<ImageDTO> => {
const blob = await this.inpaintMask.renderer.getBlob({ rect }); const blob = await this.inpaintMask.renderer.getBlob({ rect });
const imageDTO = await this.util.uploadImage(blob, 'inpaint-mask.png', 'mask', true); const imageDTO = await uploadImage(blob, 'inpaint-mask.png', 'mask', true);
this.stateApi.setInpaintMaskImageCache(imageDTO); this.stateApi.setInpaintMaskImageCache(imageDTO);
return imageDTO; return imageDTO;
}; };

View File

@ -116,7 +116,7 @@ export class CanvasObjectRenderer {
} }
this.subscriptions.add( this.subscriptions.add(
this.manager.toolState.subscribe((newVal, oldVal) => { this.manager.$toolState.listen((newVal, oldVal) => {
if (newVal.selected !== oldVal.selected) { if (newVal.selected !== oldVal.selected) {
this.commitBuffer(); this.commitBuffer();
} }

View File

@ -118,7 +118,7 @@ export class CanvasTool {
); );
this.subscriptions.add( this.subscriptions.add(
this.manager.toolState.subscribe(() => { this.manager.$toolState.listen(() => {
this.render(); this.render();
}) })
); );
@ -175,7 +175,7 @@ export class CanvasTool {
} else if (!isDrawableEntity) { } else if (!isDrawableEntity) {
// Non-drawable layers don't have tools // Non-drawable layers don't have tools
stage.container().style.cursor = 'not-allowed'; stage.container().style.cursor = 'not-allowed';
} else if (tool === 'move' || Boolean(this.manager.transformingEntity.getValue())) { } else if (tool === 'move' || Boolean(this.manager.$transformingEntity.get())) {
// Move tool gets a pointer // Move tool gets a pointer
stage.container().style.cursor = 'default'; stage.container().style.cursor = 'default';
} else if (tool === 'rect') { } else if (tool === 'rect') {

View File

@ -384,7 +384,7 @@ export class CanvasTransformer {
// When the selected tool changes, we need to update the transformer's interaction state. // When the selected tool changes, we need to update the transformer's interaction state.
this.subscriptions.add( this.subscriptions.add(
this.manager.toolState.subscribe((newVal, oldVal) => { this.manager.$toolState.listen((newVal, oldVal) => {
if (newVal.selected !== oldVal.selected) { if (newVal.selected !== oldVal.selected) {
this.syncInteractionState(); this.syncInteractionState();
} }
@ -393,7 +393,7 @@ export class CanvasTransformer {
// When the selected entity changes, we need to update the transformer's interaction state. // When the selected entity changes, we need to update the transformer's interaction state.
this.subscriptions.add( this.subscriptions.add(
this.manager.selectedEntityIdentifier.subscribe(() => { this.manager.$selectedEntityIdentifier.listen(() => {
this.syncInteractionState(); this.syncInteractionState();
}) })
); );