Skip to content

Commit 2f925e8

Browse files
committed
INT-3274: Simplified the ScriptLoader to use plain rxjs
1 parent 2d06fde commit 2f925e8

File tree

1 file changed

+23
-26
lines changed

1 file changed

+23
-26
lines changed

tinymce-angular-component/src/main/ts/utils/ScriptLoader.ts

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,44 @@
66
*
77
*/
88

9-
import { defer, fromEvent, Observable, map, shareReplay, take } from 'rxjs';
10-
11-
export interface IStateObj {
12-
script$: Observable<void> | null;
13-
}
14-
15-
const createState = (): IStateObj => ({
16-
script$: null,
17-
});
9+
import { fromEvent, Observable, shareReplay, switchMap, BehaviorSubject, first, filter, map } from 'rxjs';
1810

1911
interface ScriptLoader {
2012
load: (doc: Document, url: string) => Observable<void>;
13+
/** Intended to only to be used by tests. */
2114
reinitialize: () => void;
2215
}
2316

24-
const CreateScriptLoader = (): ScriptLoader => {
25-
let state = createState();
17+
const firstEmission = () => (source: Observable<unknown>): Observable<void> => source.pipe(first(), map(() => undefined));
2618

27-
const load = (doc: Document, url: string) => (
28-
state.script$ ||
29-
// Caretaker note: the `script$` is a multicast observable since it's piped with `shareReplay`,
30-
// so if there're multiple editor components simultaneously on the page, they'll subscribe to the internal
31-
// `ReplaySubject`. The script will be loaded only once, and `ReplaySubject` will cache the result.
32-
(state.script$ = defer(() => {
19+
const CreateScriptLoader = (): ScriptLoader => {
20+
const params$ = new BehaviorSubject<Parameters<ScriptLoader['load']> | null>(null);
21+
const loaded$: Observable<void> = params$.pipe(
22+
filter(Boolean),
23+
switchMap(([ doc, url ]) => {
3324
const scriptTag = doc.createElement('script');
3425
scriptTag.referrerPolicy = 'origin';
3526
scriptTag.type = 'application/javascript';
3627
scriptTag.src = url;
3728
doc.head.appendChild(scriptTag);
38-
return fromEvent(scriptTag, 'load').pipe(take(1), map(() => undefined));
39-
}).pipe(shareReplay({ bufferSize: 1, refCount: true })))
29+
return fromEvent(scriptTag, 'load').pipe(firstEmission());
30+
}),
31+
// Caretaker note: `loaded$` is a multicast observable since it's piped with `shareReplay`,
32+
// so if there're multiple editor components simultaneously on the page, they'll subscribe to the internal
33+
// `ReplaySubject`. The script will be loaded only once, and `ReplaySubject` will cache the result.
34+
shareReplay({ bufferSize: 1, refCount: true })
4035
);
4136

42-
// Only to be used by tests.
43-
const reinitialize = () => {
44-
state = createState();
45-
};
46-
4737
return {
48-
load,
49-
reinitialize,
38+
load: (...args) => {
39+
if (!params$.getValue()) {
40+
params$.next(args);
41+
}
42+
return loaded$;
43+
},
44+
reinitialize: () => {
45+
params$.next(null);
46+
},
5047
};
5148
};
5249

0 commit comments

Comments
 (0)