@microsoft/live-share-react 1.0.0-preview.21 → 1.0.0-preview.23

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.
Files changed (64) hide show
  1. package/README.md +1 -1
  2. package/bin/internal/errors.d.ts +26 -0
  3. package/bin/internal/errors.d.ts.map +1 -0
  4. package/bin/internal/errors.js +39 -0
  5. package/bin/internal/errors.js.map +1 -0
  6. package/bin/internal/index.d.ts +4 -0
  7. package/bin/internal/index.d.ts.map +1 -0
  8. package/bin/internal/index.js +4 -0
  9. package/bin/internal/index.js.map +1 -0
  10. package/bin/internal/type-guards.d.ts +6 -0
  11. package/bin/internal/type-guards.d.ts.map +1 -0
  12. package/bin/internal/type-guards.js +8 -0
  13. package/bin/internal/type-guards.js.map +1 -0
  14. package/bin/internal/useSharedStateRegistry.d.ts +31 -0
  15. package/bin/internal/useSharedStateRegistry.d.ts.map +1 -0
  16. package/bin/internal/useSharedStateRegistry.js +102 -0
  17. package/bin/internal/useSharedStateRegistry.js.map +1 -0
  18. package/bin/live-hooks/useLiveEvent.d.ts.map +1 -1
  19. package/bin/live-hooks/useLiveEvent.js +9 -3
  20. package/bin/live-hooks/useLiveEvent.js.map +1 -1
  21. package/bin/live-hooks/useLivePresence.d.ts +2 -2
  22. package/bin/live-hooks/useLivePresence.d.ts.map +1 -1
  23. package/bin/live-hooks/useLivePresence.js +14 -14
  24. package/bin/live-hooks/useLivePresence.js.map +1 -1
  25. package/bin/live-hooks/useLiveState.d.ts +2 -2
  26. package/bin/live-hooks/useLiveState.d.ts.map +1 -1
  27. package/bin/live-hooks/useLiveState.js +15 -8
  28. package/bin/live-hooks/useLiveState.js.map +1 -1
  29. package/bin/live-hooks/useLiveTimer.d.ts.map +1 -1
  30. package/bin/live-hooks/useLiveTimer.js +21 -15
  31. package/bin/live-hooks/useLiveTimer.js.map +1 -1
  32. package/bin/live-hooks/useMediaSynchronizer.d.ts.map +1 -1
  33. package/bin/live-hooks/useMediaSynchronizer.js +31 -12
  34. package/bin/live-hooks/useMediaSynchronizer.js.map +1 -1
  35. package/bin/providers/AzureProvider.d.ts +1 -1
  36. package/bin/providers/AzureProvider.d.ts.map +1 -1
  37. package/bin/providers/AzureProvider.js +1 -1
  38. package/bin/providers/AzureProvider.js.map +1 -1
  39. package/bin/providers/LiveShareProvider.js +1 -1
  40. package/bin/providers/LiveShareProvider.js.map +1 -1
  41. package/bin/shared-hooks/useSharedMap.d.ts.map +1 -1
  42. package/bin/shared-hooks/useSharedMap.js +15 -8
  43. package/bin/shared-hooks/useSharedMap.js.map +1 -1
  44. package/bin/shared-hooks/useSharedState.d.ts.map +1 -1
  45. package/bin/shared-hooks/useSharedState.js +9 -3
  46. package/bin/shared-hooks/useSharedState.js.map +1 -1
  47. package/bin/types/ActionTypes.d.ts +8 -2
  48. package/bin/types/ActionTypes.d.ts.map +1 -1
  49. package/package.json +8 -8
  50. package/src/internal/errors.ts +48 -0
  51. package/src/internal/index.ts +3 -0
  52. package/src/internal/type-guards.ts +9 -0
  53. package/src/live-hooks/useLiveEvent.ts +21 -5
  54. package/src/live-hooks/useLivePresence.ts +27 -21
  55. package/src/live-hooks/useLiveState.ts +31 -17
  56. package/src/live-hooks/useLiveTimer.ts +32 -26
  57. package/src/live-hooks/useMediaSynchronizer.ts +60 -8
  58. package/src/providers/AzureProvider.tsx +1 -1
  59. package/src/providers/LiveShareProvider.tsx +1 -1
  60. package/src/shared-hooks/useSharedMap.ts +29 -10
  61. package/src/shared-hooks/useSharedState.ts +9 -3
  62. package/src/types/ActionTypes.ts +10 -2
  63. package/src/internal-hooks/index.ts +0 -1
  64. /package/src/{internal-hooks → internal}/useSharedStateRegistry.ts +0 -0
@@ -7,6 +7,13 @@ import { LiveState, UserMeetingRole } from "@microsoft/live-share";
7
7
  import React from "react";
8
8
  import { SetLiveStateAction } from "../types";
9
9
  import { useDynamicDDS } from "../shared-hooks";
10
+ import { useFluidObjectsContext } from "../providers";
11
+ import {
12
+ ActionContainerNotJoinedError,
13
+ ActionLiveDataObjectInitializedError,
14
+ ActionLiveDataObjectUndefinedError,
15
+ isPrevStateCallback,
16
+ } from "../internal";
10
17
 
11
18
  /**
12
19
  * React hook for using a Live Share `LiveState`.
@@ -20,11 +27,11 @@ import { useDynamicDDS } from "../shared-hooks";
20
27
  * @returns ordered values: first value is the synchronized state value and the second is a setter to change the state value.
21
28
  * The setter returns a void promise, which will throw if the user does not have the required roles to set.
22
29
  */
23
- export function useLiveState<TState = any>(
30
+ export function useLiveState<TState>(
24
31
  uniqueKey: string,
25
32
  initialState: TState,
26
33
  allowedRoles?: UserMeetingRole[]
27
- ): [TState, SetLiveStateAction<TState>] {
34
+ ): [TState, SetLiveStateAction<TState>, LiveState<TState> | undefined] {
28
35
  const [currentState, setCurrentState] =
29
36
  React.useState<TState>(initialState);
30
37
  /**
@@ -35,32 +42,39 @@ export function useLiveState<TState = any>(
35
42
  LiveState<TState>
36
43
  );
37
44
 
45
+ const { container } = useFluidObjectsContext();
46
+
38
47
  /**
39
48
  * Change state callback that is user facing
40
49
  * @param state TState to set
41
50
  * @returns void promise, which will throw if the user does not have the required roles
42
51
  */
43
52
  const setState = React.useCallback(
44
- async (state: TState) => {
45
- if (!liveState) {
46
- console.error(
47
- new Error(
48
- "Cannot call changeState when liveState is undefined"
49
- )
53
+ async (state: TState | ((prevState: TState) => TState)) => {
54
+ if (!container) {
55
+ throw new ActionContainerNotJoinedError(
56
+ "liveState",
57
+ "setState"
58
+ );
59
+ }
60
+ if (liveState === undefined) {
61
+ throw new ActionLiveDataObjectUndefinedError(
62
+ "liveState",
63
+ "setState"
50
64
  );
51
- return;
52
65
  }
53
66
  if (!liveState.isInitialized) {
54
- console.error(
55
- new Error(
56
- "Cannot call changeState while liveState is not started"
57
- )
67
+ throw new ActionLiveDataObjectInitializedError(
68
+ "liveState",
69
+ "setState"
58
70
  );
59
- return;
60
71
  }
61
- return await liveState.set(state);
72
+ const valueToSet = isPrevStateCallback<TState>(state)
73
+ ? state(liveState.state)
74
+ : state;
75
+ return await liveState.set(valueToSet);
62
76
  },
63
- [liveState]
77
+ [container, liveState]
64
78
  );
65
79
 
66
80
  /**
@@ -85,5 +99,5 @@ export function useLiveState<TState = any>(
85
99
  };
86
100
  }, [liveState]);
87
101
 
88
- return [currentState, setState];
102
+ return [currentState, setState, liveState];
89
103
  }
@@ -22,6 +22,12 @@ import {
22
22
  OnTimerTickAction,
23
23
  } from "../types";
24
24
  import { useDynamicDDS } from "../shared-hooks";
25
+ import { useFluidObjectsContext } from "../providers";
26
+ import {
27
+ ActionContainerNotJoinedError,
28
+ ActionLiveDataObjectInitializedError,
29
+ ActionLiveDataObjectUndefinedError,
30
+ } from "../internal";
25
31
 
26
32
  export function useLiveTimer(
27
33
  uniqueKey: string,
@@ -46,6 +52,8 @@ export function useLiveTimer(
46
52
  */
47
53
  const { dds: liveTimer } = useDynamicDDS<LiveTimer>(uniqueKey, LiveTimer);
48
54
 
55
+ const { container } = useFluidObjectsContext();
56
+
49
57
  /**
50
58
  * Callback to send event through `LiveTimer`
51
59
  * @param duration the duration for the timer in milliseconds
@@ -53,23 +61,24 @@ export function useLiveTimer(
53
61
  */
54
62
  const start: OnStartTimerAction = React.useCallback(
55
63
  async (duration: number) => {
64
+ if (!container) {
65
+ throw new ActionContainerNotJoinedError("liveTimer", "start");
66
+ }
56
67
  if (liveTimer === undefined) {
57
- console.error(
58
- new Error("Cannot call start when liveTimer is undefined")
68
+ throw new ActionLiveDataObjectUndefinedError(
69
+ "liveTimer",
70
+ "start"
59
71
  );
60
- return;
61
72
  }
62
73
  if (!liveTimer.isInitialized) {
63
- console.error(
64
- new Error(
65
- "Cannot call start while liveTimer is not started"
66
- )
74
+ throw new ActionLiveDataObjectInitializedError(
75
+ "liveTimer",
76
+ "start"
67
77
  );
68
- return;
69
78
  }
70
79
  return await liveTimer.start(duration);
71
80
  },
72
- [liveTimer]
81
+ [container, liveTimer]
73
82
  );
74
83
 
75
84
  /**
@@ -77,40 +86,37 @@ export function useLiveTimer(
77
86
  * @returns void promise that will throw when user does not have required roles
78
87
  */
79
88
  const play: OnPlayTimerAction = React.useCallback(async () => {
89
+ if (!container) {
90
+ throw new ActionContainerNotJoinedError("liveTimer", "play");
91
+ }
80
92
  if (liveTimer === undefined) {
81
- console.error(
82
- new Error("Cannot call play when liveTimer is undefined")
83
- );
84
- return;
93
+ throw new ActionLiveDataObjectUndefinedError("liveTimer", "play");
85
94
  }
86
95
  if (!liveTimer.isInitialized) {
87
- console.error(
88
- new Error("Cannot call play while liveTimer is not started")
89
- );
90
- return;
96
+ throw new ActionLiveDataObjectInitializedError("liveTimer", "play");
91
97
  }
92
98
  return await liveTimer.play();
93
- }, [liveTimer]);
99
+ }, [container, liveTimer]);
94
100
 
95
101
  /**
96
102
  * Callback to send event through `LiveTimer`
97
103
  * @returns void promise that will throw when user does not have required roles
98
104
  */
99
105
  const pause: OnPauseTimerAction = React.useCallback(async () => {
106
+ if (!container) {
107
+ throw new ActionContainerNotJoinedError("liveTimer", "pause");
108
+ }
100
109
  if (liveTimer === undefined) {
101
- console.error(
102
- new Error("Cannot call pause when liveTimer is undefined")
103
- );
104
- return;
110
+ throw new ActionLiveDataObjectUndefinedError("liveTimer", "pause");
105
111
  }
106
112
  if (!liveTimer.isInitialized) {
107
- console.error(
108
- new Error("Cannot call pause while liveTimer is not started")
113
+ throw new ActionLiveDataObjectInitializedError(
114
+ "liveTimer",
115
+ "pause"
109
116
  );
110
- return;
111
117
  }
112
118
  return await liveTimer.pause();
113
- }, [liveTimer]);
119
+ }, [container, liveTimer]);
114
120
 
115
121
  /**
116
122
  * Setup change listeners and start `LiveTimer` if needed
@@ -16,6 +16,12 @@ import {
16
16
  import { isExtendedMediaMetadata, isMediaElement, isRefObject } from "../utils";
17
17
  import { useDynamicDDS } from "../shared-hooks";
18
18
  import { IUseMediaSynchronizerResults } from "../types";
19
+ import { useFluidObjectsContext } from "../providers";
20
+ import {
21
+ ActionContainerNotJoinedError,
22
+ ActionLiveDataObjectInitializedError,
23
+ ActionLiveDataObjectUndefinedError,
24
+ } from "../internal";
19
25
 
20
26
  /**
21
27
  * React hook for using a Live Share media `MediaPlayerSynchronizer`.
@@ -57,31 +63,66 @@ export function useMediaSynchronizer(
57
63
  LiveMediaSession
58
64
  );
59
65
 
66
+ const { container } = useFluidObjectsContext();
67
+
60
68
  /**
61
69
  * Play callback
62
70
  */
63
71
  const play = React.useCallback(async () => {
64
- if (!mediaSynchronizer) return;
72
+ if (!container) {
73
+ throw new ActionContainerNotJoinedError(
74
+ "mediaSynchronizer",
75
+ "play"
76
+ );
77
+ }
78
+ if (mediaSynchronizer === undefined) {
79
+ throw new ActionLiveDataObjectUndefinedError(
80
+ "mediaSynchronizer",
81
+ "play"
82
+ );
83
+ }
65
84
  return await mediaSynchronizer.play();
66
- }, [mediaSynchronizer]);
85
+ }, [container, mediaSynchronizer]);
67
86
 
68
87
  /**
69
88
  * Pause callback
70
89
  */
71
90
  const pause = React.useCallback(async () => {
72
- if (!mediaSynchronizer) return;
91
+ if (!container) {
92
+ throw new ActionContainerNotJoinedError(
93
+ "mediaSynchronizer",
94
+ "pause"
95
+ );
96
+ }
97
+ if (mediaSynchronizer === undefined) {
98
+ throw new ActionLiveDataObjectUndefinedError(
99
+ "mediaSynchronizer",
100
+ "pause"
101
+ );
102
+ }
73
103
  return await mediaSynchronizer.pause();
74
- }, [mediaSynchronizer]);
104
+ }, [container, mediaSynchronizer]);
75
105
 
76
106
  /**
77
107
  * User facing: seek callback
78
108
  */
79
109
  const seekTo = React.useCallback(
80
110
  async (time: number) => {
81
- if (!mediaSynchronizer) return;
111
+ if (!container) {
112
+ throw new ActionContainerNotJoinedError(
113
+ "mediaSynchronizer",
114
+ "seekTo"
115
+ );
116
+ }
117
+ if (mediaSynchronizer === undefined) {
118
+ throw new ActionLiveDataObjectUndefinedError(
119
+ "mediaSynchronizer",
120
+ "seekTo"
121
+ );
122
+ }
82
123
  return await mediaSynchronizer.seekTo(time);
83
124
  },
84
- [mediaSynchronizer]
125
+ [container, mediaSynchronizer]
85
126
  );
86
127
 
87
128
  /**
@@ -89,7 +130,18 @@ export function useMediaSynchronizer(
89
130
  */
90
131
  const setTrack = React.useCallback(
91
132
  async (track: Partial<ExtendedMediaMetadata> | string | null) => {
92
- if (!mediaSynchronizer) return;
133
+ if (!container) {
134
+ throw new ActionContainerNotJoinedError(
135
+ "mediaSynchronizer",
136
+ "setTrack"
137
+ );
138
+ }
139
+ if (mediaSynchronizer === undefined) {
140
+ throw new ActionLiveDataObjectUndefinedError(
141
+ "mediaSynchronizer",
142
+ "setTrack"
143
+ );
144
+ }
93
145
  if (isExtendedMediaMetadata(track)) {
94
146
  return await mediaSynchronizer.setTrack(track);
95
147
  } else if (typeof track === "string") {
@@ -98,7 +150,7 @@ export function useMediaSynchronizer(
98
150
  } as ExtendedMediaMetadata);
99
151
  }
100
152
  },
101
- [mediaSynchronizer]
153
+ [container, mediaSynchronizer]
102
154
  );
103
155
 
104
156
  /**
@@ -13,7 +13,7 @@ import { IAzureContainerResults } from "../types";
13
13
  import {
14
14
  ISharedStateRegistryResponse,
15
15
  useSharedStateRegistry,
16
- } from "../internal-hooks";
16
+ } from "../internal";
17
17
  import {
18
18
  AzureTurboClient,
19
19
  IFluidTurboClient,
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { IFluidContainer, LoadableObjectClassRecord } from "fluid-framework";
7
7
  import React from "react";
8
- import { useSharedStateRegistry } from "../internal-hooks";
8
+ import { useSharedStateRegistry } from "../internal";
9
9
  import {
10
10
  ILiveShareClientOptions,
11
11
  ILiveShareHost,
@@ -8,6 +8,11 @@ import { isEntries, isJSON, isMap } from "../utils";
8
8
  import { IUseSharedMapResults, SharedMapInitialData } from "../types";
9
9
  import { useDynamicDDS } from "./useDynamicDDS";
10
10
  import { SharedMap } from "fluid-framework";
11
+ import { useFluidObjectsContext } from "../providers";
12
+ import {
13
+ ActionContainerNotJoinedError,
14
+ ActionLiveDataObjectUndefinedError,
15
+ } from "../internal";
11
16
 
12
17
  /**
13
18
  * React hook for using a Fluid `SharedMap`.
@@ -56,20 +61,28 @@ export function useSharedMap<TData extends object = object>(
56
61
  onFirstInitialize
57
62
  );
58
63
 
64
+ const { container } = useFluidObjectsContext();
65
+
59
66
  /**
60
67
  * User facing: set a value to the Fluid `SharedMap`.
61
68
  */
62
69
  const setEntry = React.useCallback(
63
70
  (key: string, value: TData) => {
64
- if (!sharedMap) {
65
- console.error(
66
- new Error("Cannot call set when sharedMap is undefined")
71
+ if (!container) {
72
+ throw new ActionContainerNotJoinedError(
73
+ "sharedMap",
74
+ "setEntry"
75
+ );
76
+ }
77
+ if (sharedMap === undefined) {
78
+ throw new ActionLiveDataObjectUndefinedError(
79
+ "sharedMap",
80
+ "setEntry"
67
81
  );
68
- return;
69
82
  }
70
83
  sharedMap.set(key, value);
71
84
  },
72
- [sharedMap]
85
+ [container, sharedMap]
73
86
  );
74
87
 
75
88
  /**
@@ -77,15 +90,21 @@ export function useSharedMap<TData extends object = object>(
77
90
  */
78
91
  const deleteEntry = React.useCallback(
79
92
  (key: string) => {
80
- if (!sharedMap) {
81
- console.error(
82
- new Error("Cannot call remove when sharedMap is undefined")
93
+ if (!container) {
94
+ throw new ActionContainerNotJoinedError(
95
+ "sharedMap",
96
+ "deleteEntry"
97
+ );
98
+ }
99
+ if (sharedMap === undefined) {
100
+ throw new ActionLiveDataObjectUndefinedError(
101
+ "sharedMap",
102
+ "deleteEntry"
83
103
  );
84
- return;
85
104
  }
86
105
  sharedMap.delete(key);
87
106
  },
88
- [sharedMap]
107
+ [container, sharedMap]
89
108
  );
90
109
 
91
110
  // Setup change listeners, initial values, etc.
@@ -7,6 +7,7 @@ import React from "react";
7
7
  import { useFluidObjectsContext } from "../providers";
8
8
  import { DisposeSharedStateAction, SetSharedStateAction } from "../types";
9
9
  import { v4 as uuid } from "uuid";
10
+ import { isPrevStateCallback } from "../internal";
10
11
 
11
12
  /**
12
13
  * Inspired by React's `useState` hook, `useSharedState` makes it easy to synchronize state in your app.
@@ -48,9 +49,14 @@ export function useSharedState<S>(
48
49
  * User facing: callback to change the shared state.
49
50
  */
50
51
  const setSharedState: SetSharedStateAction<S> = React.useCallback(
51
- (updatedState: S) => {
52
- setLocalState(updatedState);
53
- updateSharedState(uniqueKey, updatedState);
52
+ (state: S | ((prevState: S) => S)) => {
53
+ setLocalState((prevState) => {
54
+ const valueToSet = isPrevStateCallback<S>(state)
55
+ ? state(prevState)
56
+ : state;
57
+ updateSharedState(uniqueKey, valueToSet);
58
+ return valueToSet;
59
+ });
54
60
  },
55
61
  [uniqueKey, setLocalState, updateSharedState]
56
62
  );
@@ -10,7 +10,15 @@ import { ITimerConfig, ILiveEvent, PresenceState } from "@microsoft/live-share";
10
10
 
11
11
  // React actions
12
12
 
13
- export type SetSharedStateAction<T> = (state: T) => void;
13
+ /**
14
+ * Callback to set shared state
15
+ */
16
+ export type SetSharedStateAction<TState> = (
17
+ state: TState | ((prevState: TState) => TState)
18
+ ) => void;
19
+ /**
20
+ * Callback to set local state
21
+ */
14
22
  export type SetLocalStateAction = React.Dispatch<React.SetStateAction<any>>;
15
23
 
16
24
  // Fluid actions
@@ -79,7 +87,7 @@ export type DisposeSharedStateAction = () => void;
79
87
  * (state: TState) => Promise<void>
80
88
  */
81
89
  export type SetLiveStateAction<TState = undefined> = (
82
- state: TState
90
+ state: TState | ((prevState: TState) => TState)
83
91
  ) => Promise<void>;
84
92
 
85
93
  /**
@@ -1 +0,0 @@
1
- export * from "./useSharedStateRegistry";