@capillarytech/creatives-library 9.1.6 → 9.1.7-alpha.sprint-bee-free-load-fix.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AppRoot.js +4 -7
- package/package.json +1 -1
- package/utils/beePluginLoader.js +48 -0
- package/utils/tests/beePluginLoader.test.js +85 -0
- package/v2Containers/BeeEditor/index.js +50 -17
- package/v2Containers/BeeEditor/index.scss +8 -0
- package/v2Containers/BeeEditor/messages.js +8 -0
- package/v2Containers/BeeEditor/test/index.test.js +71 -0
- package/v2Containers/BeePopupEditor/index.js +23 -11
package/AppRoot.js
CHANGED
|
@@ -14,6 +14,7 @@ import { translationMessages } from './i18n';
|
|
|
14
14
|
import initialState from './initialState';
|
|
15
15
|
import pathConfig from './config/path';
|
|
16
16
|
import { isMFEMode } from './utils/mfeDetect';
|
|
17
|
+
import { loadBeePlugin } from './utils/beePluginLoader';
|
|
17
18
|
import { emitFirstPaintReadyIfNotDataLanding } from './utils/mfeFirstPaintReady';
|
|
18
19
|
import rebaseHistory from './utils/mfeHistory';
|
|
19
20
|
import { MFEEventBus } from '@capillarytech/cap-ui-utils';
|
|
@@ -86,14 +87,10 @@ const AppRoot = ({ basename: _basename = '/creatives/ui', history: hostHistory }
|
|
|
86
87
|
}
|
|
87
88
|
}, []);
|
|
88
89
|
|
|
89
|
-
//
|
|
90
|
-
// (declared in creatives/index.html) is never loaded. Inject it here so BeeEditor works.
|
|
90
|
+
// Preload BeePlugin.js in MFE mode (host index.html lacks it); the editors also load it on demand
|
|
91
91
|
useEffect(() => {
|
|
92
|
-
if (isMFEMode()
|
|
93
|
-
|
|
94
|
-
script.src = 'https://app-rsrc.getbee.io/plugin/BeePlugin.js';
|
|
95
|
-
script.type = 'text/javascript';
|
|
96
|
-
document.head.appendChild(script);
|
|
92
|
+
if (isMFEMode()) {
|
|
93
|
+
loadBeePlugin().catch(() => {});
|
|
97
94
|
}
|
|
98
95
|
}, []);
|
|
99
96
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const BEE_PLUGIN_SCRIPT_URL = 'https://app-rsrc.getbee.io/plugin/BeePlugin.js';
|
|
2
|
+
export const BEE_PLUGIN_LOAD_TIMEOUT_MS = 30000;
|
|
3
|
+
|
|
4
|
+
let beePluginPromise = null;
|
|
5
|
+
|
|
6
|
+
// Hosts consuming creatives as a library or MFE may not ship BeePlugin.js, so load it on demand
|
|
7
|
+
export const loadBeePlugin = () => {
|
|
8
|
+
if (window.BeePlugin) {
|
|
9
|
+
return Promise.resolve(window.BeePlugin);
|
|
10
|
+
}
|
|
11
|
+
if (beePluginPromise) {
|
|
12
|
+
return beePluginPromise;
|
|
13
|
+
}
|
|
14
|
+
beePluginPromise = new Promise((resolve, reject) => {
|
|
15
|
+
const existingScript = document.querySelector('script[src*="BeePlugin.js"]');
|
|
16
|
+
const script = existingScript || document.createElement('script');
|
|
17
|
+
let timeoutId = null;
|
|
18
|
+
const fail = (error) => {
|
|
19
|
+
clearTimeout(timeoutId);
|
|
20
|
+
beePluginPromise = null;
|
|
21
|
+
// Drop the tag so a retry injects a fresh one instead of waiting on a dead script
|
|
22
|
+
script.remove();
|
|
23
|
+
reject(error);
|
|
24
|
+
};
|
|
25
|
+
const onLoad = () => {
|
|
26
|
+
if (window.BeePlugin) {
|
|
27
|
+
clearTimeout(timeoutId);
|
|
28
|
+
resolve(window.BeePlugin);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
fail(new Error('BeePlugin.js loaded but window.BeePlugin is undefined'));
|
|
32
|
+
};
|
|
33
|
+
const onError = () => fail(new Error(`Failed to load ${BEE_PLUGIN_SCRIPT_URL}`));
|
|
34
|
+
timeoutId = setTimeout(
|
|
35
|
+
() => fail(new Error(`Timed out loading ${BEE_PLUGIN_SCRIPT_URL}`)),
|
|
36
|
+
BEE_PLUGIN_LOAD_TIMEOUT_MS,
|
|
37
|
+
);
|
|
38
|
+
script.addEventListener('load', onLoad, { once: true });
|
|
39
|
+
script.addEventListener('error', onError, { once: true });
|
|
40
|
+
if (!existingScript) {
|
|
41
|
+
script.src = BEE_PLUGIN_SCRIPT_URL;
|
|
42
|
+
script.type = 'text/javascript';
|
|
43
|
+
script.async = true;
|
|
44
|
+
document.head.appendChild(script);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return beePluginPromise;
|
|
48
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const SCRIPT_SELECTOR = 'script[src*="BeePlugin.js"]';
|
|
2
|
+
|
|
3
|
+
const getLoader = () => {
|
|
4
|
+
let loader;
|
|
5
|
+
jest.isolateModules(() => {
|
|
6
|
+
loader = require('../beePluginLoader');
|
|
7
|
+
});
|
|
8
|
+
return loader;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
describe('loadBeePlugin', () => {
|
|
12
|
+
const mockBeePlugin = { create: jest.fn() };
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
delete window.BeePlugin;
|
|
16
|
+
document.head.innerHTML = '';
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
jest.useRealTimers();
|
|
21
|
+
delete window.BeePlugin;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('resolves immediately without injecting a script when BeePlugin is already loaded', async () => {
|
|
25
|
+
window.BeePlugin = mockBeePlugin;
|
|
26
|
+
const { loadBeePlugin } = getLoader();
|
|
27
|
+
await expect(loadBeePlugin()).resolves.toBe(mockBeePlugin);
|
|
28
|
+
expect(document.querySelector(SCRIPT_SELECTOR)).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('injects the script once and resolves when it loads', async () => {
|
|
32
|
+
const { loadBeePlugin, BEE_PLUGIN_SCRIPT_URL } = getLoader();
|
|
33
|
+
const firstLoad = loadBeePlugin();
|
|
34
|
+
const secondLoad = loadBeePlugin();
|
|
35
|
+
const scripts = document.querySelectorAll(SCRIPT_SELECTOR);
|
|
36
|
+
expect(scripts).toHaveLength(1);
|
|
37
|
+
expect(scripts[0].src).toBe(BEE_PLUGIN_SCRIPT_URL);
|
|
38
|
+
expect(secondLoad).toBe(firstLoad);
|
|
39
|
+
window.BeePlugin = mockBeePlugin;
|
|
40
|
+
scripts[0].dispatchEvent(new Event('load'));
|
|
41
|
+
await expect(firstLoad).resolves.toBe(mockBeePlugin);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('waits on a host-provided script instead of injecting another', async () => {
|
|
45
|
+
const hostScript = document.createElement('script');
|
|
46
|
+
hostScript.src = 'https://app-rsrc.getbee.io/plugin/BeePlugin.js';
|
|
47
|
+
document.head.appendChild(hostScript);
|
|
48
|
+
const { loadBeePlugin } = getLoader();
|
|
49
|
+
const load = loadBeePlugin();
|
|
50
|
+
expect(document.querySelectorAll(SCRIPT_SELECTOR)).toHaveLength(1);
|
|
51
|
+
window.BeePlugin = mockBeePlugin;
|
|
52
|
+
hostScript.dispatchEvent(new Event('load'));
|
|
53
|
+
await expect(load).resolves.toBe(mockBeePlugin);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('rejects on script error, removes the tag and allows a retry', async () => {
|
|
57
|
+
const { loadBeePlugin } = getLoader();
|
|
58
|
+
const failedLoad = loadBeePlugin();
|
|
59
|
+
document.querySelector(SCRIPT_SELECTOR).dispatchEvent(new Event('error'));
|
|
60
|
+
await expect(failedLoad).rejects.toThrow('Failed to load');
|
|
61
|
+
expect(document.querySelector(SCRIPT_SELECTOR)).toBeNull();
|
|
62
|
+
|
|
63
|
+
const retryLoad = loadBeePlugin();
|
|
64
|
+
expect(retryLoad).not.toBe(failedLoad);
|
|
65
|
+
window.BeePlugin = mockBeePlugin;
|
|
66
|
+
document.querySelector(SCRIPT_SELECTOR).dispatchEvent(new Event('load'));
|
|
67
|
+
await expect(retryLoad).resolves.toBe(mockBeePlugin);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('rejects when the script loads but does not define BeePlugin', async () => {
|
|
71
|
+
const { loadBeePlugin } = getLoader();
|
|
72
|
+
const load = loadBeePlugin();
|
|
73
|
+
document.querySelector(SCRIPT_SELECTOR).dispatchEvent(new Event('load'));
|
|
74
|
+
await expect(load).rejects.toThrow('window.BeePlugin is undefined');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('rejects after the timeout when the script never settles', async () => {
|
|
78
|
+
jest.useFakeTimers();
|
|
79
|
+
const { loadBeePlugin, BEE_PLUGIN_LOAD_TIMEOUT_MS } = getLoader();
|
|
80
|
+
const load = loadBeePlugin();
|
|
81
|
+
jest.advanceTimersByTime(BEE_PLUGIN_LOAD_TIMEOUT_MS);
|
|
82
|
+
await expect(load).rejects.toThrow('Timed out');
|
|
83
|
+
expect(document.querySelector(SCRIPT_SELECTOR)).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -19,7 +19,9 @@ import CapInput from "@capillarytech/cap-ui-library/CapInput";
|
|
|
19
19
|
import CapSelect from "@capillarytech/cap-ui-library/CapSelect";
|
|
20
20
|
import CapSpin from "@capillarytech/cap-ui-library/CapSpin";
|
|
21
21
|
import CapNotification from "@capillarytech/cap-ui-library/CapNotification";
|
|
22
|
+
import CapError from "@capillarytech/cap-ui-library/CapError";
|
|
22
23
|
import config from '../../config/app';
|
|
24
|
+
import { loadBeePlugin } from '../../utils/beePluginLoader';
|
|
23
25
|
import makeSelectBEE from './selectors';
|
|
24
26
|
import messages from './messages';
|
|
25
27
|
import * as beeActions from './actions';
|
|
@@ -75,6 +77,9 @@ function BeeEditor(props) {
|
|
|
75
77
|
const [rowMetaInfo, setRowMetaInfo] = useState({});
|
|
76
78
|
const [rowName, setRowName] = useState('');
|
|
77
79
|
const [rowCategory, setRowCategory] = useState('');
|
|
80
|
+
const [isBeePluginLoading, setIsBeePluginLoading] = useState(false);
|
|
81
|
+
const [hasBeePluginLoadError, setHasBeePluginLoadError] = useState(false);
|
|
82
|
+
const [beePluginLoadAttempt, setBeePluginLoadAttempt] = useState(0);
|
|
78
83
|
const beePluginInstanceRef = useRef(null);
|
|
79
84
|
const initializedBeeJsonRef = useRef(null); // Track which beeJson we've initialized for
|
|
80
85
|
const isInitializingRef = useRef(false); // Track if initialization is in progress
|
|
@@ -258,23 +263,37 @@ function BeeEditor(props) {
|
|
|
258
263
|
beePluginInstanceRef.current = null;
|
|
259
264
|
}
|
|
260
265
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
let isEffectCancelled = false;
|
|
267
|
+
setHasBeePluginLoadError(false);
|
|
268
|
+
setIsBeePluginLoading(!window.BeePlugin);
|
|
269
|
+
loadBeePlugin()
|
|
270
|
+
.then((BeePlugin) => {
|
|
271
|
+
if (isEffectCancelled) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
setIsBeePluginLoading(false);
|
|
275
|
+
BeePlugin.create(currentTokenData, beeConfig, (instance) => {
|
|
276
|
+
beePluginInstance = instance;
|
|
277
|
+
beePluginInstanceRef.current = instance;
|
|
278
|
+
const parseJson = typeof beeJson === 'string' ? JSON.parse(beeJson) : beeJson;
|
|
279
|
+
beePluginInstance.start(parseJson);
|
|
280
|
+
saveBeeInstance(beePluginInstance);
|
|
281
|
+
// Mark this beeJson as initialized and clear initialization flag
|
|
282
|
+
initializedBeeJsonRef.current = initializationKey;
|
|
283
|
+
isInitializingRef.current = false;
|
|
284
|
+
});
|
|
285
|
+
})
|
|
286
|
+
.catch((error) => {
|
|
287
|
+
if (isEffectCancelled) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
console.error('[BeeEditor] Error creating BEE plugin:', error);
|
|
291
|
+
setIsBeePluginLoading(false);
|
|
292
|
+
setHasBeePluginLoadError(true);
|
|
270
293
|
isInitializingRef.current = false;
|
|
271
294
|
});
|
|
272
|
-
} catch (error) {
|
|
273
|
-
// Reset flag on error
|
|
274
|
-
console.error('[BeeEditor] Error creating BEE plugin:', error);
|
|
275
|
-
isInitializingRef.current = false;
|
|
276
|
-
}
|
|
277
295
|
return () => {
|
|
296
|
+
isEffectCancelled = true;
|
|
278
297
|
clearInterval(intervalTimer);
|
|
279
298
|
// Reset initialization flag on cleanup
|
|
280
299
|
isInitializingRef.current = false;
|
|
@@ -293,7 +312,12 @@ function BeeEditor(props) {
|
|
|
293
312
|
};
|
|
294
313
|
// Only depend on beeJson, uid, and id - not tokenData
|
|
295
314
|
// tokenData is accessed via ref to prevent re-initialization when it changes
|
|
296
|
-
}, [beeJson, uid, id]);
|
|
315
|
+
}, [beeJson, uid, id, beePluginLoadAttempt]);
|
|
316
|
+
|
|
317
|
+
const onRetryBeePluginLoad = () => {
|
|
318
|
+
setBeePluginLoadAttempt((attempt) => attempt + 1);
|
|
319
|
+
};
|
|
320
|
+
|
|
297
321
|
const callbackSaveRow = (status, errorMsg) => {
|
|
298
322
|
if (status === 'success') {
|
|
299
323
|
CapNotification.success({message: formatMessage(messages.rowCreationSuccessMgs), key: 'create-custom-row-success'});
|
|
@@ -367,8 +391,17 @@ function BeeEditor(props) {
|
|
|
367
391
|
</>
|
|
368
392
|
);
|
|
369
393
|
return (
|
|
370
|
-
<CapSpin spinning={saveRowRequest ||
|
|
371
|
-
|
|
394
|
+
<CapSpin spinning={saveRowRequest || isBeePluginLoading}>
|
|
395
|
+
{hasBeePluginLoadError ? (
|
|
396
|
+
<div className="bee-plugin-load-error">
|
|
397
|
+
<CapError type="error">{formatMessage(messages.beePluginLoadError)}</CapError>
|
|
398
|
+
<CapButton type="secondary" onClick={onRetryBeePluginLoad}>
|
|
399
|
+
{formatMessage(messages.retry)}
|
|
400
|
+
</CapButton>
|
|
401
|
+
</div>
|
|
402
|
+
) : (
|
|
403
|
+
<div id="bee-plugin-container" style={{ height: "650px" }}></div>
|
|
404
|
+
)}
|
|
372
405
|
<TagList
|
|
373
406
|
moduleFilterEnabled={moduleFilterEnabled}
|
|
374
407
|
label={label}
|
|
@@ -64,4 +64,12 @@ export default defineMessages({
|
|
|
64
64
|
id: `${prefix}.rowCreationFailureMgs`,
|
|
65
65
|
defaultMessage: 'Error in row creation',
|
|
66
66
|
},
|
|
67
|
+
beePluginLoadError: {
|
|
68
|
+
id: `${prefix}.beePluginLoadError`,
|
|
69
|
+
defaultMessage: 'Unable to load the drag & drop editor. Check your network connection and try again.',
|
|
70
|
+
},
|
|
71
|
+
retry: {
|
|
72
|
+
id: `${prefix}.retry`,
|
|
73
|
+
defaultMessage: 'Retry',
|
|
74
|
+
},
|
|
67
75
|
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '@testing-library/jest-dom';
|
|
3
|
+
import { render, screen, fireEvent, waitFor } from '../../../utils/test-utils';
|
|
4
|
+
import { loadBeePlugin } from '../../../utils/beePluginLoader';
|
|
5
|
+
import BeeEditor from '../index';
|
|
6
|
+
|
|
7
|
+
jest.mock('../../../utils/beePluginLoader', () => ({
|
|
8
|
+
loadBeePlugin: jest.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
jest.mock('../../TagList', () => () => null);
|
|
12
|
+
|
|
13
|
+
const mockBeePluginInstance = { start: jest.fn(), destroy: jest.fn() };
|
|
14
|
+
const mockBeePlugin = { create: jest.fn() };
|
|
15
|
+
|
|
16
|
+
const defaultProps = {
|
|
17
|
+
uid: 'test-uid',
|
|
18
|
+
id: 'bee-editor',
|
|
19
|
+
beeJson: '{"page":{}}',
|
|
20
|
+
tokenData: { access_token: 'token' },
|
|
21
|
+
saveBeeInstance: jest.fn(),
|
|
22
|
+
saveBeeData: jest.fn(),
|
|
23
|
+
tags: [],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe('BeeEditor BeePlugin loading', () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
mockBeePlugin.create.mockImplementation((tokenData, beeConfig, onCreated) => onCreated(mockBeePluginInstance));
|
|
29
|
+
jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
console.error.mockRestore();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('creates the BEE editor once the plugin script has loaded', async () => {
|
|
37
|
+
loadBeePlugin.mockResolvedValue(mockBeePlugin);
|
|
38
|
+
const { container } = render(<BeeEditor {...defaultProps} />);
|
|
39
|
+
await waitFor(() => expect(mockBeePlugin.create).toHaveBeenCalledTimes(1));
|
|
40
|
+
expect(mockBeePluginInstance.start).toHaveBeenCalledWith({ page: {} });
|
|
41
|
+
expect(defaultProps.saveBeeInstance).toHaveBeenCalledWith(mockBeePluginInstance);
|
|
42
|
+
expect(container.querySelector('#bee-plugin-container')).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('shows an error with retry when the plugin script fails, and recovers on retry', async () => {
|
|
46
|
+
loadBeePlugin
|
|
47
|
+
.mockRejectedValueOnce(new Error('Failed to load'))
|
|
48
|
+
.mockResolvedValueOnce(mockBeePlugin);
|
|
49
|
+
const { container } = render(<BeeEditor {...defaultProps} />);
|
|
50
|
+
|
|
51
|
+
expect(await screen.findByText(/Unable to load the drag & drop editor/)).toBeInTheDocument();
|
|
52
|
+
expect(container.querySelector('#bee-plugin-container')).not.toBeInTheDocument();
|
|
53
|
+
expect(mockBeePlugin.create).not.toHaveBeenCalled();
|
|
54
|
+
|
|
55
|
+
fireEvent.click(screen.getByRole('button', { name: 'Retry' }));
|
|
56
|
+
|
|
57
|
+
await waitFor(() => expect(mockBeePlugin.create).toHaveBeenCalledTimes(1));
|
|
58
|
+
expect(screen.queryByText(/Unable to load the drag & drop editor/)).not.toBeInTheDocument();
|
|
59
|
+
expect(container.querySelector('#bee-plugin-container')).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('does not create the editor if it unmounts before the plugin loads', async () => {
|
|
63
|
+
let resolveLoad;
|
|
64
|
+
loadBeePlugin.mockReturnValue(new Promise((resolve) => { resolveLoad = resolve; }));
|
|
65
|
+
const { unmount } = render(<BeeEditor {...defaultProps} />);
|
|
66
|
+
unmount();
|
|
67
|
+
resolveLoad(mockBeePlugin);
|
|
68
|
+
await Promise.resolve();
|
|
69
|
+
expect(mockBeePlugin.create).not.toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -6,6 +6,7 @@ import { connect } from 'react-redux';
|
|
|
6
6
|
import { createStructuredSelector } from 'reselect';
|
|
7
7
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
8
8
|
import { UserIsAuthenticated } from '../../utils/authWrapper';
|
|
9
|
+
import { loadBeePlugin } from '../../utils/beePluginLoader';
|
|
9
10
|
import TagList from '../TagList';
|
|
10
11
|
import {
|
|
11
12
|
ANDROID, BEE_LAYOUT_OPTIONS, MOBILE, UNSUBSCRIBE,
|
|
@@ -113,18 +114,29 @@ function BeePopupEditor(props) {
|
|
|
113
114
|
saveBeeData(jsonFile, htmlFile, device);
|
|
114
115
|
},
|
|
115
116
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
let isEffectCancelled = false;
|
|
118
|
+
loadBeePlugin()
|
|
119
|
+
.then((BeePlugin) => {
|
|
120
|
+
if (isEffectCancelled) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
BeePlugin.create(tokenData, beeConfig, (instance) => {
|
|
124
|
+
beePluginInstance = instance;
|
|
125
|
+
beeInstanceRef.current = instance;
|
|
126
|
+
// Use ref to get the latest beeJson — the closure captures the value at effect time,
|
|
127
|
+
// but beeJsonRef.current is always up-to-date (e.g. when template data loads async)
|
|
128
|
+
const latestBeeJson = beeJsonRef.current;
|
|
129
|
+
const parseJson = typeof latestBeeJson === 'string' ? JSON.parse(latestBeeJson) : latestBeeJson;
|
|
130
|
+
beePluginInstance.start(parseJson);
|
|
131
|
+
saveBeeInstance(beePluginInstance, device);
|
|
132
|
+
isInitializedRef.current = true;
|
|
133
|
+
});
|
|
134
|
+
})
|
|
135
|
+
.catch((error) => {
|
|
136
|
+
console.error('[BeePopupEditor] Error creating BEE plugin:', error);
|
|
137
|
+
});
|
|
127
138
|
return () => {
|
|
139
|
+
isEffectCancelled = true;
|
|
128
140
|
clearInterval(intervalTimer);
|
|
129
141
|
// Reset initialization flag when component unmounts or layout changes
|
|
130
142
|
isInitializedRef.current = false;
|