@player-ui/react 0.12.0 → 0.12.1-next.0

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.
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { useSyncExternalStore } from "use-sync-external-store/shim";
2
3
  import type {
3
4
  FlowManager,
4
5
  ManagedPlayerProps,
@@ -20,10 +21,7 @@ interface ManagedPlayerStateKey {
20
21
  _key: symbol;
21
22
  }
22
23
 
23
- export interface StateChangeCallback {
24
- /** Trigger for state changes */
25
- onState: (s: ManagedPlayerState) => void;
26
- }
24
+ export type StateChangeCallback = (state?: ManagedPlayerState) => void;
27
25
 
28
26
  /**
29
27
  * An object to store the state of the managed player
@@ -106,7 +104,9 @@ class ManagedState {
106
104
  private async setState(state: ManagedPlayerState) {
107
105
  this.state = state;
108
106
  this.callbacks.forEach((c) => {
109
- c.onState(state);
107
+ if (c && typeof c === "function") {
108
+ c(this.state);
109
+ }
110
110
  });
111
111
 
112
112
  const { manager, reactPlayer, playerConfig } = state.context;
@@ -208,6 +208,12 @@ const managedPlayerStateMachines = new WeakMap<
208
208
  ManagedState
209
209
  >();
210
210
 
211
+ function createKey(): ManagedPlayerStateKey {
212
+ return {
213
+ _key: Symbol("managed-player"),
214
+ };
215
+ }
216
+
211
217
  /** Creates an x-state state machine that persists when this component is no longer renders (due to Suspense) */
212
218
  export const usePersistentStateMachine = (options: {
213
219
  /** the flow manager to use */
@@ -219,29 +225,90 @@ export const usePersistentStateMachine = (options: {
219
225
  /** Any middleware for the manager */
220
226
  middleware?: ManagerMiddleware;
221
227
  }): { managedState: ManagedState; state?: ManagedPlayerState } => {
222
- const keyRef = React.useRef<ManagedPlayerStateKey>({
223
- _key: Symbol("managed-player"),
224
- });
228
+ const mounted = React.useRef(false);
229
+ const previousManager = React.useRef(options.manager);
230
+ const keyRef = React.useRef<ManagedPlayerStateKey>(createKey());
231
+ const managedStateRef = React.useRef(
232
+ new ManagedState({ middleware: options.middleware }),
233
+ );
234
+
235
+ if (!mounted.current) {
236
+ managedPlayerStateMachines.set(keyRef.current, managedStateRef.current);
237
+ mounted.current = true;
238
+ }
225
239
 
226
- const managedState =
227
- managedPlayerStateMachines.get(keyRef.current) ??
228
- new ManagedState({ middleware: options.middleware });
229
- managedPlayerStateMachines.set(keyRef.current, managedState);
230
- const [state, setState] = React.useState(managedState.state);
240
+ if (previousManager.current !== options.manager) {
241
+ const oldManagedState = managedPlayerStateMachines.get(keyRef.current);
242
+
243
+ /**
244
+ * We have to handle terminate here as well as the useEffect in the
245
+ * ManagedPlayer since it won't have the instance of the previous manager
246
+ */
247
+ if (oldManagedState) {
248
+ const playerState =
249
+ oldManagedState.state?.context.reactPlayer.player.getState();
250
+
251
+ if (
252
+ oldManagedState.state?.value === "running" &&
253
+ playerState?.status === "in-progress"
254
+ ) {
255
+ previousManager.current.terminate?.(
256
+ playerState.controllers.data.serialize(),
257
+ );
258
+ }
259
+ }
231
260
 
232
- React.useEffect(() => {
233
- const unsub = managedState.addListener({
234
- onState: (s) => {
235
- setState(s);
236
- },
261
+ const newKey = createKey();
262
+ const newManagedState = new ManagedState({
263
+ middleware: options.middleware,
237
264
  });
238
265
 
239
- if (managedState.state === undefined) {
240
- managedState.start(options);
266
+ managedPlayerStateMachines.set(newKey, newManagedState);
267
+ keyRef.current = newKey;
268
+ managedStateRef.current = newManagedState;
269
+ previousManager.current = options.manager;
270
+ }
271
+
272
+ const managedState =
273
+ managedPlayerStateMachines.get(keyRef.current) ?? managedStateRef.current;
274
+
275
+ /**
276
+ * There are times where the managedState the external store references no
277
+ * longer exists, so we have to wrap instead of calling addListener directly.
278
+ */
279
+ function subscription(callback: (val?: ManagedPlayerState) => void) {
280
+ if (managedState) {
281
+ const unsub = managedState.addListener((s) => {
282
+ callback(s);
283
+ });
284
+
285
+ return () => {
286
+ if (managedState) {
287
+ unsub();
288
+ }
289
+ };
241
290
  }
242
291
 
243
- return unsub;
244
- }, []);
292
+ return () => {};
293
+ }
294
+
295
+ function getSnapshot() {
296
+ return managedState.state;
297
+ }
298
+
299
+ const state = useSyncExternalStore(
300
+ subscription,
301
+ getSnapshot,
302
+ () => undefined,
303
+ );
304
+
305
+ /**
306
+ * This needs to come after useSyncExternalStore, otherwise it causes
307
+ * a weird state update and none of the refs in this hook persist
308
+ */
309
+ if (managedState.state === undefined) {
310
+ managedState.start(options);
311
+ }
245
312
 
246
313
  return { managedState, state };
247
314
  };
@@ -1,10 +1,7 @@
1
1
  import React from "react";
2
2
  import type { FlowManager, ManagedPlayerProps, ManagedPlayerState, ManagerMiddleware } from "./types";
3
3
  import type { ReactPlayerOptions } from "../player";
4
- export interface StateChangeCallback {
5
- /** Trigger for state changes */
6
- onState: (s: ManagedPlayerState) => void;
7
- }
4
+ export type StateChangeCallback = (state?: ManagedPlayerState) => void;
8
5
  /**
9
6
  * An object to store the state of the managed player
10
7
  */