fix(sticky): prevent crash on editor re-entry during sticky lifecycle

Clear editor state before scene callbacks to prevent blur timer or
startEditing re-entry from calling updateFromData on a destroyed sprite.
Guard all display object access with .destroyed checks.
This commit is contained in:
Hiren Kangad
2026-03-12 22:09:12 +05:30
parent a191f74c31
commit 565b7c6c19
+55 -44
View File
@@ -201,52 +201,12 @@ export class TextEditor {
const item = this._item;
if (!ta || !item) return;
if (save) {
const onChange = this._onChange;
const originalText = this._originalText;
const newText = ta.value;
if (item.type === 'sticky' && item.displayObject instanceof StickySprite) {
const data = item.data as StickyObject;
data.text = newText;
item.displayObject.updateFromData(data);
data.h = item.displayObject.computedHeight;
item.displayObject.showText(true);
this._onChange?.();
} else if (item.type === 'text') {
const pixiText = item.displayObject;
if (pixiText instanceof Text) {
const data = item.data as TextObject;
data.text = newText;
pixiText.text = newText;
pixiText.visible = true;
const bounds = pixiText.getLocalBounds();
data.w = bounds.width;
data.h = bounds.height;
this._onChange?.();
}
}
} else {
// Cancel — restore original
if (item.type === 'sticky' && item.displayObject instanceof StickySprite) {
const data = item.data as StickyObject;
data.text = this._originalText;
item.displayObject.updateFromData(data);
data.h = item.displayObject.computedHeight;
item.displayObject.showText(true);
} else if (item.type === 'text') {
const pixiText = item.displayObject;
if (pixiText instanceof Text) {
const data = item.data as TextObject;
data.text = this._originalText;
pixiText.text = this._originalText;
pixiText.visible = true;
}
}
// Always notify on cancel too — the tool callback needs this
// to clean up empty newly-created items (sticky or text).
this._onChange?.();
}
// DOM cleanup
// Clear editor state before mutating scene objects or invoking callbacks.
// This prevents blur/startEditing re-entry from acting on a destroyed item.
ta.remove();
this._textarea = null;
this._item = null;
@@ -254,5 +214,56 @@ export class TextEditor {
this._container = null;
this._onChange = null;
this._originalText = '';
if (save) {
if (
item.type === 'sticky' &&
item.displayObject instanceof StickySprite &&
!item.displayObject.destroyed
) {
const data = item.data as StickyObject;
data.text = newText;
item.displayObject.updateFromData(data);
data.h = item.displayObject.computedHeight;
item.displayObject.showText(true);
onChange?.();
} else if (item.type === 'text') {
const pixiText = item.displayObject;
if (pixiText instanceof Text && !pixiText.destroyed) {
const data = item.data as TextObject;
data.text = newText;
pixiText.text = newText;
pixiText.visible = true;
const bounds = pixiText.getLocalBounds();
data.w = bounds.width;
data.h = bounds.height;
onChange?.();
}
}
} else {
// Cancel — restore original
if (
item.type === 'sticky' &&
item.displayObject instanceof StickySprite &&
!item.displayObject.destroyed
) {
const data = item.data as StickyObject;
data.text = originalText;
item.displayObject.updateFromData(data);
data.h = item.displayObject.computedHeight;
item.displayObject.showText(true);
} else if (item.type === 'text') {
const pixiText = item.displayObject;
if (pixiText instanceof Text && !pixiText.destroyed) {
const data = item.data as TextObject;
data.text = originalText;
pixiText.text = originalText;
pixiText.visible = true;
}
}
// Always notify on cancel too — the tool callback needs this
// to clean up empty newly-created items (sticky or text).
onChange?.();
}
}
}