From bdd4a330583e57cf9eda798453d4815cfe4f9d84 Mon Sep 17 00:00:00 2001 From: nd0ut Date: Fri, 1 Dec 2023 17:41:53 +0800 Subject: [PATCH 1/7] fix(block): destroy both local and global contexts --- abstract/Block.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/abstract/Block.js b/abstract/Block.js index b0c5d57d9..995c828e0 100644 --- a/abstract/Block.js +++ b/abstract/Block.js @@ -1,5 +1,5 @@ // @ts-check -import { BaseComponent } from '@symbiotejs/symbiote'; +import { BaseComponent, Data } from '@symbiotejs/symbiote'; import { EventEmitter } from '../blocks/UploadCtxProvider/EventEmitter.js'; import { createWindowHeightTracker, getIsWindowHeightTracked } from '../utils/createWindowHeightTracker.js'; import { getPluralForm } from '../utils/getPluralForm.js'; @@ -188,8 +188,18 @@ export class Block extends BaseComponent { } destroyCallback() { + /** @type {Set} */ let blocksRegistry = this.$['*blocksRegistry']; blocksRegistry.delete(this); + + // Destroy local context + // TODO: this should be done inside symbiote + Data.deleteCtx(this); + + if (blocksRegistry.size === 0) { + // Destroy external context if there is no any blocks left inside it + Data.deleteCtx(this.ctxName); + } } /** From deb9d25e6ea60e17fa2682a077a1a74e2de77645 Mon Sep 17 00:00:00 2001 From: nd0ut Date: Fri, 1 Dec 2023 17:42:06 +0800 Subject: [PATCH 2/7] fix(cloud-image-editor): destroy context --- blocks/CloudImageEditor/src/CloudImageEditorBlock.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/blocks/CloudImageEditor/src/CloudImageEditorBlock.js b/blocks/CloudImageEditor/src/CloudImageEditorBlock.js index 9c094c300..c1a653984 100644 --- a/blocks/CloudImageEditor/src/CloudImageEditorBlock.js +++ b/blocks/CloudImageEditor/src/CloudImageEditorBlock.js @@ -16,6 +16,7 @@ import { parseTabs } from './lib/parseTabs.js'; import { initState } from './state.js'; import { TEMPLATE } from './template.js'; import { TabId } from './toolbar-constants.js'; +import { Data } from '@symbiotejs/symbiote'; export class CloudImageEditorBlock extends CloudImageEditorBase { static className = 'cloud-image-editor'; @@ -78,6 +79,13 @@ export class CloudImageEditorBlock extends CloudImageEditorBase { this.initEditor(); } + destroyCallback() { + super.destroyCallback(); + + // Destroy global editor's context + Data.deleteCtx(this.ctxName); + } + async updateImage() { await this._waitForSize(); From ad89e56e0771ae064df9fb00996de69f2fe35323 Mon Sep 17 00:00:00 2001 From: nd0ut Date: Fri, 1 Dec 2023 17:42:43 +0800 Subject: [PATCH 3/7] fix(drop-area): destroy global registry context if there are no items inside --- blocks/DropArea/DropArea.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/blocks/DropArea/DropArea.js b/blocks/DropArea/DropArea.js index 3ca283f05..3357d4f2f 100644 --- a/blocks/DropArea/DropArea.js +++ b/blocks/DropArea/DropArea.js @@ -1,5 +1,6 @@ // @ts-check +import { Data } from '@symbiotejs/symbiote'; import { ActivityBlock } from '../../abstract/ActivityBlock.js'; import { UploaderBlock } from '../../abstract/UploaderBlock.js'; import { stringToArray } from '../../utils/stringToArray.js'; @@ -7,6 +8,9 @@ import { asBoolean } from '../Config/normalizeConfigValue.js'; import { UploadSource } from '../utils/UploadSource.js'; import { DropzoneState, addDropzone } from './addDropzone.js'; +const GLOBAL_CTX_NAME = 'lr-drop-area'; +const REGISTRY_KEY = `${GLOBAL_CTX_NAME}/registry`; + export class DropArea extends UploaderBlock { constructor() { super(); @@ -20,7 +24,7 @@ export class DropArea extends UploaderBlock { isEnabled: true, isVisible: true, text: this.l10n('drop-files-here'), - 'lr-drop-area/targets': null, + [REGISTRY_KEY]: null, }; } @@ -45,10 +49,10 @@ export class DropArea extends UploaderBlock { initCallback() { super.initCallback(); - if (!this.$['lr-drop-area/targets']) { - this.$['lr-drop-area/targets'] = new Set(); + if (!this.$[REGISTRY_KEY]) { + this.$[REGISTRY_KEY] = new Set(); } - this.$['lr-drop-area/targets'].add(this); + this.$[REGISTRY_KEY].add(this); this.defineAccessor( 'disabled', @@ -182,7 +186,7 @@ export class DropArea extends UploaderBlock { if (!this.$.isFullscreen) { return false; } - const otherTargets = [...this.$['lr-drop-area/targets']].filter((el) => el !== this); + const otherTargets = [...this.$[REGISTRY_KEY]].filter((el) => el !== this); const activeTargets = otherTargets.filter((/** @type {typeof this} */ el) => { return el.isActive(); }); @@ -209,7 +213,14 @@ export class DropArea extends UploaderBlock { destroyCallback() { super.destroyCallback(); - this.$['lr-drop-area/targets']?.remove?.(this); + /** @type {Set} */ + const registry = this.$[REGISTRY_KEY]; + if (registry) { + registry.delete(this); + if (registry.size === 0) { + Data.deleteCtx(GLOBAL_CTX_NAME); + } + } this._destroyDropzone?.(); this._destroyContentWrapperDropzone?.(); From 05ba451ba139c860e701fe57195883c0f104534a Mon Sep 17 00:00:00 2001 From: nd0ut Date: Fri, 1 Dec 2023 17:42:56 +0800 Subject: [PATCH 4/7] fix(img): destroy context --- blocks/Img/ImgBase.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blocks/Img/ImgBase.js b/blocks/Img/ImgBase.js index cd75a762d..9343517da 100644 --- a/blocks/Img/ImgBase.js +++ b/blocks/Img/ImgBase.js @@ -1,4 +1,4 @@ -import { BaseComponent } from '@symbiotejs/symbiote'; +import { BaseComponent, Data } from '@symbiotejs/symbiote'; import { applyTemplateData } from '../../utils/template-utils.js'; import { createCdnUrl, createCdnUrlModifiers, createOriginalUrl } from '../../utils/cdn-utils.js'; import { PROPS_MAP } from './props-map.js'; @@ -339,6 +339,7 @@ export class ImgBase extends BaseComponent { }); this._isnObserver = null; } + Data.deleteCtx(this); } static get observedAttributes() { From a7c136d9d5a79eff68874c559276c1a0d84d658b Mon Sep 17 00:00:00 2001 From: nd0ut Date: Fri, 1 Dec 2023 17:43:12 +0800 Subject: [PATCH 5/7] fix(typed-collection): destroy context --- abstract/TypedCollection.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/abstract/TypedCollection.js b/abstract/TypedCollection.js index bf9f58812..986deb39f 100644 --- a/abstract/TypedCollection.js +++ b/abstract/TypedCollection.js @@ -117,7 +117,7 @@ export class TypedCollection { /** @param {Function} handler */ unobserveCollection(handler) { - this.__collectionObservers.delete(handler); + this.__collectionObservers?.delete(handler); } /** @@ -201,7 +201,7 @@ export class TypedCollection { /** @param {Function} handler */ unobserveProperties(handler) { - this.__propertyObservers.delete(handler); + this.__propertyObservers?.delete(handler); } /** @@ -228,7 +228,7 @@ export class TypedCollection { } destroy() { - Data.deleteCtx(this.__data); + Data.deleteCtx(this.__ctxId); this.__propertyObservers = null; this.__collectionObservers = null; for (let id in this.__subsMap) { From bcc4d460fd7724a0d84cd4c40a065c67a6479d18 Mon Sep 17 00:00:00 2001 From: nd0ut Date: Fri, 1 Dec 2023 17:43:40 +0800 Subject: [PATCH 6/7] fix(upload-collection): destroy upload collection --- abstract/UploaderBlock.js | 4 +++- blocks/CameraSource/CameraSource.js | 1 + .../src/CloudImageEditorBlock.js | 10 +------- .../CloudImageEditorActivity.js | 1 + blocks/ExternalSource/ExternalSource.js | 1 + blocks/FileItem/FileItem.js | 1 + blocks/SimpleBtn/SimpleBtn.js | 1 + blocks/SourceBtn/SourceBtn.js | 1 + blocks/UploadCtxProvider/EventEmitter.js | 23 +++++++++++++------ blocks/UploadCtxProvider/UploadCtxProvider.js | 6 +++++ blocks/UploadList/UploadList.js | 3 +++ blocks/UrlSource/UrlSource.js | 1 + 12 files changed, 36 insertions(+), 17 deletions(-) diff --git a/abstract/UploaderBlock.js b/abstract/UploaderBlock.js index 91e11eacb..3b181a0bc 100644 --- a/abstract/UploaderBlock.js +++ b/abstract/UploaderBlock.js @@ -19,6 +19,7 @@ import { TypedCollection } from './TypedCollection.js'; import { uploadEntrySchema } from './uploadEntrySchema.js'; export class UploaderBlock extends ActivityBlock { + couldBeCtxOwner = false; isCtxOwner = false; init$ = uploaderBlockCtx(this); @@ -84,7 +85,7 @@ export class UploaderBlock extends ActivityBlock { this.add('*uploadCollection', uploadCollection); } - if (!this.hasCtxOwner) { + if (!this.hasCtxOwner && this.couldBeCtxOwner) { this.initCtxOwner(); } } @@ -94,6 +95,7 @@ export class UploaderBlock extends ActivityBlock { if (this.isCtxOwner) { this._unobserveCollectionProperties?.(); this._unobserveCollection?.(); + this.uploadCollection.destroy(); } } diff --git a/blocks/CameraSource/CameraSource.js b/blocks/CameraSource/CameraSource.js index a72a323f7..c5a241382 100644 --- a/blocks/CameraSource/CameraSource.js +++ b/blocks/CameraSource/CameraSource.js @@ -5,6 +5,7 @@ import { debounce } from '../utils/debounce.js'; import { UploadSource } from '../utils/UploadSource.js'; export class CameraSource extends UploaderBlock { + couldBeCtxOwner = true; activityType = ActivityBlock.activities.CAMERA; /** @private */ diff --git a/blocks/CloudImageEditor/src/CloudImageEditorBlock.js b/blocks/CloudImageEditor/src/CloudImageEditorBlock.js index c1a653984..349bc9764 100644 --- a/blocks/CloudImageEditor/src/CloudImageEditorBlock.js +++ b/blocks/CloudImageEditor/src/CloudImageEditorBlock.js @@ -11,12 +11,11 @@ import { debounce } from '../../utils/debounce.js'; import { CloudImageEditorBase } from './CloudImageEditorBase.js'; import { classNames } from './lib/classNames.js'; import { parseCropPreset } from './lib/parseCropPreset.js'; -import { operationsToTransformations, transformationsToOperations } from './lib/transformationUtils.js'; import { parseTabs } from './lib/parseTabs.js'; +import { operationsToTransformations, transformationsToOperations } from './lib/transformationUtils.js'; import { initState } from './state.js'; import { TEMPLATE } from './template.js'; import { TabId } from './toolbar-constants.js'; -import { Data } from '@symbiotejs/symbiote'; export class CloudImageEditorBlock extends CloudImageEditorBase { static className = 'cloud-image-editor'; @@ -79,13 +78,6 @@ export class CloudImageEditorBlock extends CloudImageEditorBase { this.initEditor(); } - destroyCallback() { - super.destroyCallback(); - - // Destroy global editor's context - Data.deleteCtx(this.ctxName); - } - async updateImage() { await this._waitForSize(); diff --git a/blocks/CloudImageEditorActivity/CloudImageEditorActivity.js b/blocks/CloudImageEditorActivity/CloudImageEditorActivity.js index 1a23093b0..2b495e4d7 100644 --- a/blocks/CloudImageEditorActivity/CloudImageEditorActivity.js +++ b/blocks/CloudImageEditorActivity/CloudImageEditorActivity.js @@ -4,6 +4,7 @@ import { UploaderBlock } from '../../abstract/UploaderBlock.js'; import { CloudImageEditorBlock } from '../CloudImageEditor/index.js'; export class CloudImageEditorActivity extends UploaderBlock { + couldBeCtxOwner = true; activityType = ActivityBlock.activities.CLOUD_IMG_EDIT; constructor() { diff --git a/blocks/ExternalSource/ExternalSource.js b/blocks/ExternalSource/ExternalSource.js index 56a55de9f..70ca3e188 100644 --- a/blocks/ExternalSource/ExternalSource.js +++ b/blocks/ExternalSource/ExternalSource.js @@ -31,6 +31,7 @@ import { queryString } from './query-string.js'; /** @typedef {SelectedFileMessage | EmbedCssMessage} Message */ export class ExternalSource extends UploaderBlock { + couldBeCtxOwner = true; activityType = ActivityBlock.activities.EXTERNAL; constructor() { diff --git a/blocks/FileItem/FileItem.js b/blocks/FileItem/FileItem.js index 0143afa79..1b48507c8 100644 --- a/blocks/FileItem/FileItem.js +++ b/blocks/FileItem/FileItem.js @@ -17,6 +17,7 @@ const FileItemState = Object.freeze({ }); export class FileItem extends UploaderBlock { + couldBeCtxOwner = true; pauseRender = true; /** @private */ diff --git a/blocks/SimpleBtn/SimpleBtn.js b/blocks/SimpleBtn/SimpleBtn.js index d1f3672c9..877f12f71 100644 --- a/blocks/SimpleBtn/SimpleBtn.js +++ b/blocks/SimpleBtn/SimpleBtn.js @@ -3,6 +3,7 @@ import { UploaderBlock } from '../../abstract/UploaderBlock.js'; import { asBoolean } from '../Config/normalizeConfigValue.js'; export class SimpleBtn extends UploaderBlock { + couldBeCtxOwner = true; constructor() { super(); diff --git a/blocks/SourceBtn/SourceBtn.js b/blocks/SourceBtn/SourceBtn.js index ccf1505d3..a4d357efd 100644 --- a/blocks/SourceBtn/SourceBtn.js +++ b/blocks/SourceBtn/SourceBtn.js @@ -4,6 +4,7 @@ import { ActivityBlock } from '../../abstract/ActivityBlock.js'; const L10N_PREFIX = 'src-type-'; export class SourceBtn extends UploaderBlock { + couldBeCtxOwner = true; /** @private */ _registeredTypes = {}; diff --git a/blocks/UploadCtxProvider/EventEmitter.js b/blocks/UploadCtxProvider/EventEmitter.js index 586dfd0d7..716a73d4c 100644 --- a/blocks/UploadCtxProvider/EventEmitter.js +++ b/blocks/UploadCtxProvider/EventEmitter.js @@ -61,6 +61,9 @@ export class EventEmitter { */ _timeoutStore = new Map(); + /** @type {Set} */ + _targets = new Set(); + /** @param {() => string} getCtxName */ constructor(getCtxName) { /** @private */ @@ -73,8 +76,12 @@ export class EventEmitter { /** @param {import('../../abstract/Block.js').Block} target */ bindTarget(target) { - /** @private */ - this._target = target; + this._targets.add(target); + } + + /** @param {import('../../abstract/Block.js').Block} target */ + unbindTarget(target) { + this._targets.delete(target); } /** @@ -84,11 +91,13 @@ export class EventEmitter { * @param {EventPayload[T]} [payload] */ _dispatch(type, payload) { - this._target?.dispatchEvent( - new CustomEvent(type, { - detail: payload, - }) - ); + for (const target of this._targets) { + target.dispatchEvent( + new CustomEvent(type, { + detail: payload, + }) + ); + } const globalEventType = GlobalEventType[type]; window.dispatchEvent( diff --git a/blocks/UploadCtxProvider/UploadCtxProvider.js b/blocks/UploadCtxProvider/UploadCtxProvider.js index 4f73364f8..f50d5233f 100644 --- a/blocks/UploadCtxProvider/UploadCtxProvider.js +++ b/blocks/UploadCtxProvider/UploadCtxProvider.js @@ -9,6 +9,12 @@ class UploadCtxProviderClass extends UploaderBlock { this.$['*eventEmitter'].bindTarget(this); } + + destroyCallback() { + super.destroyCallback(); + + this.$['*eventEmitter'].unbindTarget(this); + } } /** diff --git a/blocks/UploadList/UploadList.js b/blocks/UploadList/UploadList.js index 9195771ab..9c20b5745 100644 --- a/blocks/UploadList/UploadList.js +++ b/blocks/UploadList/UploadList.js @@ -16,6 +16,9 @@ import { debounce } from '../utils/debounce.js'; */ export class UploadList extends UploaderBlock { + // Context owner should have access to CSS l10n + // TODO: We need to move away l10n from CSS + couldBeCtxOwner = true; historyTracked = true; activityType = ActivityBlock.activities.UPLOAD_LIST; diff --git a/blocks/UrlSource/UrlSource.js b/blocks/UrlSource/UrlSource.js index 31270113e..22a30feac 100644 --- a/blocks/UrlSource/UrlSource.js +++ b/blocks/UrlSource/UrlSource.js @@ -3,6 +3,7 @@ import { ActivityBlock } from '../../abstract/ActivityBlock.js'; import { UploadSource } from '../utils/UploadSource.js'; export class UrlSource extends UploaderBlock { + couldBeCtxOwner = true; activityType = ActivityBlock.activities.URL; init$ = { From c1f6a0f1bfe5ec509e66a1a55603fed82f4f3175 Mon Sep 17 00:00:00 2001 From: nd0ut Date: Fri, 1 Dec 2023 17:44:06 +0800 Subject: [PATCH 7/7] fix(config): do not overwrite config values inside context --- blocks/Config/Config.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/blocks/Config/Config.js b/blocks/Config/Config.js index 6f6f6e851..3fbd9ceff 100644 --- a/blocks/Config/Config.js +++ b/blocks/Config/Config.js @@ -39,7 +39,6 @@ const attrStateMapping = /** @type {Record