@microsoft/live-share-react 1.0.0-preview.9 → 1.0.1-preview.1
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/README.md +99 -35
- package/bin/index.d.ts +6 -6
- package/bin/index.js +6 -6
- package/bin/interfaces/LiveInterfaceExtensions.d.ts +25 -8
- package/bin/interfaces/LiveInterfaceExtensions.d.ts.map +1 -1
- package/bin/interfaces/LiveInterfaceExtensions.js +5 -5
- package/bin/interfaces/index.d.ts +1 -1
- package/bin/interfaces/index.js +1 -1
- package/bin/internal/errors.d.ts +26 -0
- package/bin/internal/errors.d.ts.map +1 -0
- package/bin/internal/errors.js +39 -0
- package/bin/internal/errors.js.map +1 -0
- package/bin/internal/index.d.ts +4 -0
- package/bin/internal/index.d.ts.map +1 -0
- package/bin/internal/index.js +4 -0
- package/bin/internal/index.js.map +1 -0
- package/bin/internal/type-guards.d.ts +6 -0
- package/bin/internal/type-guards.d.ts.map +1 -0
- package/bin/internal/type-guards.js +8 -0
- package/bin/internal/type-guards.js.map +1 -0
- package/bin/{internal-hooks → internal}/useSharedStateRegistry.d.ts +30 -30
- package/bin/internal/useSharedStateRegistry.d.ts.map +1 -0
- package/bin/{internal-hooks → internal}/useSharedStateRegistry.js +101 -99
- package/bin/internal/useSharedStateRegistry.js.map +1 -0
- package/bin/live-hooks/index.d.ts +6 -6
- package/bin/live-hooks/index.js +6 -6
- package/bin/live-hooks/useLiveCanvas.d.ts +27 -27
- package/bin/live-hooks/useLiveCanvas.d.ts.map +1 -1
- package/bin/live-hooks/useLiveCanvas.js +158 -166
- package/bin/live-hooks/useLiveCanvas.js.map +1 -1
- package/bin/live-hooks/useLiveEvent.d.ts +21 -21
- package/bin/live-hooks/useLiveEvent.d.ts.map +1 -1
- package/bin/live-hooks/useLiveEvent.js +100 -85
- package/bin/live-hooks/useLiveEvent.js.map +1 -1
- package/bin/live-hooks/useLivePresence.d.ts +25 -25
- package/bin/live-hooks/useLivePresence.d.ts.map +1 -1
- package/bin/live-hooks/useLivePresence.js +106 -92
- package/bin/live-hooks/useLivePresence.js.map +1 -1
- package/bin/live-hooks/useLiveState.d.ts +19 -19
- package/bin/live-hooks/useLiveState.d.ts.map +1 -1
- package/bin/live-hooks/useLiveState.js +79 -70
- package/bin/live-hooks/useLiveState.js.map +1 -1
- package/bin/live-hooks/useLiveTimer.d.ts +7 -7
- package/bin/live-hooks/useLiveTimer.d.ts.map +1 -1
- package/bin/live-hooks/useLiveTimer.js +150 -131
- package/bin/live-hooks/useLiveTimer.js.map +1 -1
- package/bin/live-hooks/useMediaSynchronizer.d.ts +22 -22
- package/bin/live-hooks/useMediaSynchronizer.d.ts.map +1 -1
- package/bin/live-hooks/useMediaSynchronizer.js +206 -161
- package/bin/live-hooks/useMediaSynchronizer.js.map +1 -1
- package/bin/providers/AzureProvider.d.ts +33 -33
- package/bin/providers/AzureProvider.d.ts.map +1 -1
- package/bin/providers/AzureProvider.js +101 -101
- package/bin/providers/AzureProvider.js.map +1 -1
- package/bin/providers/LiveShareProvider.d.ts +62 -41
- package/bin/providers/LiveShareProvider.d.ts.map +1 -1
- package/bin/providers/LiveShareProvider.js +68 -65
- package/bin/providers/LiveShareProvider.js.map +1 -1
- package/bin/providers/index.d.ts +2 -2
- package/bin/providers/index.js +2 -2
- package/bin/shared-hooks/index.d.ts +3 -4
- package/bin/shared-hooks/index.d.ts.map +1 -1
- package/bin/shared-hooks/index.js +3 -4
- package/bin/shared-hooks/index.js.map +1 -1
- package/bin/shared-hooks/useDynamicDDS.d.ts +18 -18
- package/bin/shared-hooks/useDynamicDDS.d.ts.map +1 -1
- package/bin/shared-hooks/useDynamicDDS.js +67 -67
- package/bin/shared-hooks/useDynamicDDS.js.map +1 -1
- package/bin/shared-hooks/useSharedMap.d.ts +21 -21
- package/bin/shared-hooks/useSharedMap.d.ts.map +1 -1
- package/bin/shared-hooks/useSharedMap.js +112 -105
- package/bin/shared-hooks/useSharedMap.js.map +1 -1
- package/bin/shared-hooks/useSharedState.d.ts +20 -20
- package/bin/shared-hooks/useSharedState.d.ts.map +1 -1
- package/bin/shared-hooks/useSharedState.js +72 -62
- package/bin/shared-hooks/useSharedState.js.map +1 -1
- package/bin/types/ActionTypes.d.ts +111 -29
- package/bin/types/ActionTypes.d.ts.map +1 -1
- package/bin/types/ActionTypes.js +5 -5
- package/bin/types/PropTypes.d.ts +7 -7
- package/bin/types/PropTypes.d.ts.map +1 -1
- package/bin/types/PropTypes.js +5 -5
- package/bin/types/ResultTypes.d.ts +174 -162
- package/bin/types/ResultTypes.d.ts.map +1 -1
- package/bin/types/ResultTypes.js +5 -5
- package/bin/types/index.d.ts +3 -3
- package/bin/types/index.js +3 -3
- package/bin/utils/index.d.ts +1 -1
- package/bin/utils/index.js +1 -1
- package/bin/utils/type-check.d.ts +14 -14
- package/bin/utils/type-check.js +53 -53
- package/package.json +18 -14
- package/src/interfaces/LiveInterfaceExtensions.ts +19 -2
- package/src/internal/errors.ts +48 -0
- package/src/internal/index.ts +3 -0
- package/src/internal/type-guards.ts +9 -0
- package/src/{internal-hooks → internal}/useSharedStateRegistry.ts +6 -5
- package/src/live-hooks/useLiveCanvas.ts +12 -20
- package/src/live-hooks/useLiveEvent.ts +41 -22
- package/src/live-hooks/useLivePresence.ts +52 -33
- package/src/live-hooks/useLiveState.ts +48 -53
- package/src/live-hooks/useLiveTimer.ts +50 -41
- package/src/live-hooks/useMediaSynchronizer.ts +108 -41
- package/src/providers/AzureProvider.tsx +3 -8
- package/src/providers/LiveShareProvider.tsx +46 -16
- package/src/shared-hooks/index.ts +0 -1
- package/src/shared-hooks/useDynamicDDS.ts +15 -8
- package/src/shared-hooks/useSharedMap.ts +48 -22
- package/src/shared-hooks/useSharedState.ts +15 -5
- package/src/types/ActionTypes.ts +100 -15
- package/src/types/ResultTypes.ts +23 -9
- package/bin/internal-hooks/index.d.ts +0 -2
- package/bin/internal-hooks/index.d.ts.map +0 -1
- package/bin/internal-hooks/index.js +0 -2
- package/bin/internal-hooks/index.js.map +0 -1
- package/bin/internal-hooks/useDynamicDDSRegistry.d.ts +0 -14
- package/bin/internal-hooks/useDynamicDDSRegistry.d.ts.map +0 -1
- package/bin/internal-hooks/useDynamicDDSRegistry.js +0 -142
- package/bin/internal-hooks/useDynamicDDSRegistry.js.map +0 -1
- package/bin/internal-hooks/useSharedStateRegistry.d.ts.map +0 -1
- package/bin/internal-hooks/useSharedStateRegistry.js.map +0 -1
- package/bin/internal-hooks/useStateRef.d.ts +0 -7
- package/bin/internal-hooks/useStateRef.d.ts.map +0 -1
- package/bin/internal-hooks/useStateRef.js +0 -15
- package/bin/internal-hooks/useStateRef.js.map +0 -1
- package/bin/live-hooks/useLiveAICompletion.d.ts +0 -29
- package/bin/live-hooks/useLiveAICompletion.d.ts.map +0 -1
- package/bin/live-hooks/useLiveAICompletion.js +0 -186
- package/bin/live-hooks/useLiveAICompletion.js.map +0 -1
- package/bin/live-hooks/useLiveCoPilot.d.ts +0 -28
- package/bin/live-hooks/useLiveCoPilot.d.ts.map +0 -1
- package/bin/live-hooks/useLiveCoPilot.js +0 -178
- package/bin/live-hooks/useLiveCoPilot.js.map +0 -1
- package/bin/live-share-turbo/AzureTurboClient.d.ts +0 -39
- package/bin/live-share-turbo/AzureTurboClient.d.ts.map +0 -1
- package/bin/live-share-turbo/AzureTurboClient.js +0 -56
- package/bin/live-share-turbo/AzureTurboClient.js.map +0 -1
- package/bin/live-share-turbo/LiveShareTurboClient.d.ts +0 -53
- package/bin/live-share-turbo/LiveShareTurboClient.d.ts.map +0 -1
- package/bin/live-share-turbo/LiveShareTurboClient.js +0 -69
- package/bin/live-share-turbo/LiveShareTurboClient.js.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboDataObject.d.ts +0 -31
- package/bin/live-share-turbo/dds-objects/TurboDataObject.d.ts.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboDataObject.js +0 -75
- package/bin/live-share-turbo/dds-objects/TurboDataObject.js.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLiveCanvas.d.ts +0 -48
- package/bin/live-share-turbo/dds-objects/TurboLiveCanvas.d.ts.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLiveCanvas.js +0 -104
- package/bin/live-share-turbo/dds-objects/TurboLiveCanvas.js.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLiveEvent.d.ts +0 -28
- package/bin/live-share-turbo/dds-objects/TurboLiveEvent.d.ts.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLiveEvent.js +0 -68
- package/bin/live-share-turbo/dds-objects/TurboLiveEvent.js.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLiveMediaSession.d.ts +0 -60
- package/bin/live-share-turbo/dds-objects/TurboLiveMediaSession.d.ts.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLiveMediaSession.js +0 -111
- package/bin/live-share-turbo/dds-objects/TurboLiveMediaSession.js.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLivePresence.d.ts +0 -77
- package/bin/live-share-turbo/dds-objects/TurboLivePresence.d.ts.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLivePresence.js +0 -137
- package/bin/live-share-turbo/dds-objects/TurboLivePresence.js.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLiveState.d.ts +0 -30
- package/bin/live-share-turbo/dds-objects/TurboLiveState.d.ts.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboLiveState.js +0 -74
- package/bin/live-share-turbo/dds-objects/TurboLiveState.js.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboSharedMap.d.ts +0 -61
- package/bin/live-share-turbo/dds-objects/TurboSharedMap.d.ts.map +0 -1
- package/bin/live-share-turbo/dds-objects/TurboSharedMap.js +0 -108
- package/bin/live-share-turbo/dds-objects/TurboSharedMap.js.map +0 -1
- package/bin/live-share-turbo/dds-objects/index.d.ts +0 -8
- package/bin/live-share-turbo/dds-objects/index.d.ts.map +0 -1
- package/bin/live-share-turbo/dds-objects/index.js +0 -8
- package/bin/live-share-turbo/dds-objects/index.js.map +0 -1
- package/bin/live-share-turbo/index.d.ts +0 -5
- package/bin/live-share-turbo/index.d.ts.map +0 -1
- package/bin/live-share-turbo/index.js +0 -5
- package/bin/live-share-turbo/index.js.map +0 -1
- package/bin/live-share-turbo/interfaces/IFluidTurboClient.d.ts +0 -15
- package/bin/live-share-turbo/interfaces/IFluidTurboClient.d.ts.map +0 -1
- package/bin/live-share-turbo/interfaces/IFluidTurboClient.js +0 -2
- package/bin/live-share-turbo/interfaces/IFluidTurboClient.js.map +0 -1
- package/bin/live-share-turbo/interfaces/index.d.ts +0 -3
- package/bin/live-share-turbo/interfaces/index.d.ts.map +0 -1
- package/bin/live-share-turbo/interfaces/index.js +0 -3
- package/bin/live-share-turbo/interfaces/index.js.map +0 -1
- package/bin/live-share-turbo/internals/FluidTurboClient.d.ts +0 -39
- package/bin/live-share-turbo/internals/FluidTurboClient.d.ts.map +0 -1
- package/bin/live-share-turbo/internals/FluidTurboClient.js +0 -121
- package/bin/live-share-turbo/internals/FluidTurboClient.js.map +0 -1
- package/bin/live-share-turbo/internals/index.d.ts +0 -2
- package/bin/live-share-turbo/internals/index.d.ts.map +0 -1
- package/bin/live-share-turbo/internals/index.js +0 -2
- package/bin/live-share-turbo/internals/index.js.map +0 -1
- package/bin/live-share-turbo/internals/types.d.ts +0 -4
- package/bin/live-share-turbo/internals/types.d.ts.map +0 -1
- package/bin/live-share-turbo/internals/types.js +0 -2
- package/bin/live-share-turbo/internals/types.js.map +0 -1
- package/bin/providers/FluidContextProvider.d.ts +0 -34
- package/bin/providers/FluidContextProvider.d.ts.map +0 -1
- package/bin/providers/FluidContextProvider.js +0 -99
- package/bin/providers/FluidContextProvider.js.map +0 -1
- package/bin/providers/LiveShareContextProvider.d.ts +0 -42
- package/bin/providers/LiveShareContextProvider.d.ts.map +0 -1
- package/bin/providers/LiveShareContextProvider.js +0 -63
- package/bin/providers/LiveShareContextProvider.js.map +0 -1
- package/bin/shared-hooks/useLockedTask.d.ts +0 -11
- package/bin/shared-hooks/useLockedTask.d.ts.map +0 -1
- package/bin/shared-hooks/useLockedTask.js +0 -82
- package/bin/shared-hooks/useLockedTask.js.map +0 -1
- package/bin/shared-hooks/useTaskManager.d.ts +0 -13
- package/bin/shared-hooks/useTaskManager.d.ts.map +0 -1
- package/bin/shared-hooks/useTaskManager.js +0 -86
- package/bin/shared-hooks/useTaskManager.js.map +0 -1
- package/bin/utils/getContainerSchema.d.ts +0 -8
- package/bin/utils/getContainerSchema.d.ts.map +0 -1
- package/bin/utils/getContainerSchema.js +0 -41
- package/bin/utils/getContainerSchema.js.map +0 -1
- package/src/internal-hooks/index.ts +0 -1
- package/src/shared-hooks/useTaskManager.ts +0 -82
- package/tinylicious.log +0 -4
|
@@ -7,14 +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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
10
|
+
import { useFluidObjectsContext } from "../providers";
|
|
11
|
+
import {
|
|
12
|
+
ActionContainerNotJoinedError,
|
|
13
|
+
ActionLiveDataObjectInitializedError,
|
|
14
|
+
ActionLiveDataObjectUndefinedError,
|
|
15
|
+
isPrevStateCallback,
|
|
16
|
+
} from "../internal";
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* React hook for using a Live Share `LiveState`.
|
|
@@ -24,57 +23,58 @@ interface ILiveStateStatus<
|
|
|
24
23
|
*
|
|
25
24
|
* @param uniqueKey the unique key for the `LiveEvent`. If one does not yet exist, a new one will be created, otherwise it will use the existing one.
|
|
26
25
|
* @param initialState Optional. the initial state value of type TState
|
|
27
|
-
* @param initialData Optional. the initial data value of type TData
|
|
28
26
|
* @param allowedRoles Optional. the user roles that are allowed to mutate the synchronized state
|
|
29
|
-
* @returns ordered values: first value is the synchronized state value
|
|
27
|
+
* @returns ordered values: first value is the synchronized state value and the second is a setter to change the state value.
|
|
28
|
+
* The setter returns a void promise, which will throw if the user does not have the required roles to set.
|
|
30
29
|
*/
|
|
31
|
-
export function useLiveState<
|
|
32
|
-
TState extends string = string,
|
|
33
|
-
TData extends object = object
|
|
34
|
-
>(
|
|
30
|
+
export function useLiveState<TState = any>(
|
|
35
31
|
uniqueKey: string,
|
|
36
|
-
initialState
|
|
37
|
-
initialData?: TData,
|
|
32
|
+
initialState: TState,
|
|
38
33
|
allowedRoles?: UserMeetingRole[]
|
|
39
|
-
): [TState
|
|
40
|
-
const [
|
|
41
|
-
|
|
42
|
-
>({
|
|
43
|
-
state: initialState,
|
|
44
|
-
data: initialData,
|
|
45
|
-
});
|
|
34
|
+
): [TState, SetLiveStateAction<TState>, LiveState<TState> | undefined] {
|
|
35
|
+
const [currentState, setCurrentState] =
|
|
36
|
+
React.useState<TState>(initialState);
|
|
46
37
|
/**
|
|
47
38
|
* User facing: dynamically load the DDS for the given unique key.
|
|
48
39
|
*/
|
|
49
|
-
const { dds: liveState } = useDynamicDDS<LiveState<
|
|
40
|
+
const { dds: liveState } = useDynamicDDS<LiveState<TState>>(
|
|
50
41
|
uniqueKey,
|
|
51
|
-
LiveState<
|
|
42
|
+
LiveState<TState>
|
|
52
43
|
);
|
|
53
44
|
|
|
45
|
+
const { container } = useFluidObjectsContext();
|
|
46
|
+
|
|
54
47
|
/**
|
|
55
48
|
* Change state callback that is user facing
|
|
49
|
+
* @param state TState to set
|
|
50
|
+
* @returns void promise, which will throw if the user does not have the required roles
|
|
56
51
|
*/
|
|
57
|
-
const
|
|
58
|
-
(state: TState
|
|
59
|
-
if (!
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
const setState = React.useCallback(
|
|
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"
|
|
64
64
|
);
|
|
65
|
-
return;
|
|
66
65
|
}
|
|
67
66
|
if (!liveState.isInitialized) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
67
|
+
throw new ActionLiveDataObjectInitializedError(
|
|
68
|
+
"liveState",
|
|
69
|
+
"setState"
|
|
72
70
|
);
|
|
73
|
-
return;
|
|
74
71
|
}
|
|
75
|
-
|
|
72
|
+
const valueToSet = isPrevStateCallback<TState>(state)
|
|
73
|
+
? state(liveState.state)
|
|
74
|
+
: state;
|
|
75
|
+
return await liveState.set(valueToSet);
|
|
76
76
|
},
|
|
77
|
-
[liveState]
|
|
77
|
+
[container, liveState]
|
|
78
78
|
);
|
|
79
79
|
|
|
80
80
|
/**
|
|
@@ -83,26 +83,21 @@ export function useLiveState<
|
|
|
83
83
|
React.useEffect(() => {
|
|
84
84
|
if (liveState === undefined) return;
|
|
85
85
|
|
|
86
|
-
const onStateChanged = (state: TState
|
|
87
|
-
|
|
88
|
-
state,
|
|
89
|
-
data,
|
|
90
|
-
});
|
|
86
|
+
const onStateChanged = (state: TState) => {
|
|
87
|
+
setCurrentState(state);
|
|
91
88
|
};
|
|
92
89
|
liveState.on("stateChanged", onStateChanged);
|
|
93
90
|
if (!liveState.isInitialized) {
|
|
94
|
-
liveState.initialize(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
} else if (liveState.state) {
|
|
99
|
-
onStateChanged(liveState.state as TState, liveState.data);
|
|
91
|
+
liveState.initialize(initialState, allowedRoles);
|
|
92
|
+
}
|
|
93
|
+
if (JSON.stringify(liveState.state) !== JSON.stringify(initialState)) {
|
|
94
|
+
onStateChanged(liveState.state);
|
|
100
95
|
}
|
|
101
96
|
|
|
102
97
|
return () => {
|
|
103
98
|
liveState?.off("stateChanged", onStateChanged);
|
|
104
99
|
};
|
|
105
|
-
}, [liveState
|
|
100
|
+
}, [liveState]);
|
|
106
101
|
|
|
107
|
-
return [
|
|
102
|
+
return [currentState, setState, liveState];
|
|
108
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,
|
|
@@ -31,7 +37,7 @@ export function useLiveTimer(
|
|
|
31
37
|
onStart?: OnTimerDidStartAction,
|
|
32
38
|
onPause?: OnTimerDidPauseAction,
|
|
33
39
|
onPlay?: OnTimerDidPlayAction,
|
|
34
|
-
onFinish?: OnTimerDidFinishAction
|
|
40
|
+
onFinish?: OnTimerDidFinishAction
|
|
35
41
|
): IUseLiveTimerResults {
|
|
36
42
|
/**
|
|
37
43
|
* User facing: Stateful timer config.
|
|
@@ -46,74 +52,77 @@ 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`
|
|
59
|
+
* @param duration the duration for the timer in milliseconds
|
|
60
|
+
* @returns void promise that will throw when user does not have required roles
|
|
51
61
|
*/
|
|
52
62
|
const start: OnStartTimerAction = React.useCallback(
|
|
53
|
-
(duration: number)
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
async (duration: number) => {
|
|
64
|
+
if (!container) {
|
|
65
|
+
throw new ActionContainerNotJoinedError("liveTimer", "start");
|
|
66
|
+
}
|
|
67
|
+
if (liveTimer === undefined) {
|
|
68
|
+
throw new ActionLiveDataObjectUndefinedError(
|
|
69
|
+
"liveTimer",
|
|
70
|
+
"start"
|
|
57
71
|
);
|
|
58
|
-
return;
|
|
59
72
|
}
|
|
60
73
|
if (!liveTimer.isInitialized) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)
|
|
74
|
+
throw new ActionLiveDataObjectInitializedError(
|
|
75
|
+
"liveTimer",
|
|
76
|
+
"start"
|
|
65
77
|
);
|
|
66
|
-
return;
|
|
67
78
|
}
|
|
68
|
-
liveTimer
|
|
79
|
+
return await liveTimer.start(duration);
|
|
69
80
|
},
|
|
70
|
-
[liveTimer
|
|
81
|
+
[container, liveTimer]
|
|
71
82
|
);
|
|
72
83
|
|
|
73
84
|
/**
|
|
74
|
-
*
|
|
85
|
+
* Callback to send event through `LiveTimer`
|
|
86
|
+
* @returns void promise that will throw when user does not have required roles
|
|
75
87
|
*/
|
|
76
|
-
const play: OnPlayTimerAction = React.useCallback(()
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
88
|
+
const play: OnPlayTimerAction = React.useCallback(async () => {
|
|
89
|
+
if (!container) {
|
|
90
|
+
throw new ActionContainerNotJoinedError("liveTimer", "play");
|
|
91
|
+
}
|
|
92
|
+
if (liveTimer === undefined) {
|
|
93
|
+
throw new ActionLiveDataObjectUndefinedError("liveTimer", "play");
|
|
82
94
|
}
|
|
83
95
|
if (!liveTimer.isInitialized) {
|
|
84
|
-
|
|
85
|
-
new Error("Cannot call play while liveTimer is not started")
|
|
86
|
-
);
|
|
87
|
-
return;
|
|
96
|
+
throw new ActionLiveDataObjectInitializedError("liveTimer", "play");
|
|
88
97
|
}
|
|
89
|
-
liveTimer
|
|
90
|
-
}, [liveTimer
|
|
98
|
+
return await liveTimer.play();
|
|
99
|
+
}, [container, liveTimer]);
|
|
91
100
|
|
|
92
101
|
/**
|
|
93
|
-
*
|
|
102
|
+
* Callback to send event through `LiveTimer`
|
|
103
|
+
* @returns void promise that will throw when user does not have required roles
|
|
94
104
|
*/
|
|
95
|
-
const pause: OnPauseTimerAction = React.useCallback(()
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
const pause: OnPauseTimerAction = React.useCallback(async () => {
|
|
106
|
+
if (!container) {
|
|
107
|
+
throw new ActionContainerNotJoinedError("liveTimer", "pause");
|
|
108
|
+
}
|
|
109
|
+
if (liveTimer === undefined) {
|
|
110
|
+
throw new ActionLiveDataObjectUndefinedError("liveTimer", "pause");
|
|
101
111
|
}
|
|
102
112
|
if (!liveTimer.isInitialized) {
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
throw new ActionLiveDataObjectInitializedError(
|
|
114
|
+
"liveTimer",
|
|
115
|
+
"pause"
|
|
105
116
|
);
|
|
106
|
-
return;
|
|
107
117
|
}
|
|
108
|
-
liveTimer
|
|
109
|
-
}, [liveTimer
|
|
118
|
+
return await liveTimer.pause();
|
|
119
|
+
}, [container, liveTimer]);
|
|
110
120
|
|
|
111
121
|
/**
|
|
112
122
|
* Setup change listeners and start `LiveTimer` if needed
|
|
113
123
|
*/
|
|
114
124
|
React.useEffect(() => {
|
|
115
|
-
if (liveTimer === undefined)
|
|
116
|
-
return;
|
|
125
|
+
if (liveTimer === undefined) return;
|
|
117
126
|
// Register event listeners
|
|
118
127
|
const onTimerConfigChange = (config: ITimerConfig) => {
|
|
119
128
|
setTimerConfig(config);
|
|
@@ -16,6 +16,11 @@ 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
|
+
ActionLiveDataObjectUndefinedError,
|
|
23
|
+
} from "../internal";
|
|
19
24
|
|
|
20
25
|
/**
|
|
21
26
|
* React hook for using a Live Share media `MediaPlayerSynchronizer`.
|
|
@@ -24,7 +29,7 @@ import { IUseMediaSynchronizerResults } from "../types";
|
|
|
24
29
|
* Use this hook if you want to synchronize the playback position of a video or audio element during a Live Share session.
|
|
25
30
|
*
|
|
26
31
|
* @param uniqueKey uniqueKey value for the `LiveMediaSession` DDS object used by `MediaPlayerSynchronizer`.
|
|
27
|
-
* @param
|
|
32
|
+
* @param mediaPlayerElement React RefObject containing object/element conforming to IMediaPlayer interface, `IMediaPlayer` object, or string id for <video> / <audio> element.
|
|
28
33
|
* @param initialTrack initial track to load. Either ExtendedMediaMetadata, trackId string, or null.
|
|
29
34
|
* @param allowedRoles Optional. Array of user roles that are eligible to modify group playback state.
|
|
30
35
|
* @param viewOnly Optional. Flag for whether or not the media synchronizer should be in viewOnly mode.
|
|
@@ -32,7 +37,11 @@ import { IUseMediaSynchronizerResults } from "../types";
|
|
|
32
37
|
*/
|
|
33
38
|
export function useMediaSynchronizer(
|
|
34
39
|
uniqueKey: string,
|
|
35
|
-
|
|
40
|
+
mediaPlayerElement:
|
|
41
|
+
| React.RefObject<IMediaPlayer>
|
|
42
|
+
| IMediaPlayer
|
|
43
|
+
| string
|
|
44
|
+
| null,
|
|
36
45
|
initialTrack: Partial<ExtendedMediaMetadata> | string | null,
|
|
37
46
|
allowedRoles?: UserMeetingRole[],
|
|
38
47
|
viewOnly?: boolean
|
|
@@ -52,47 +61,99 @@ export function useMediaSynchronizer(
|
|
|
52
61
|
/**
|
|
53
62
|
* User facing: dynamically load the LiveMediaSession DDS for the given unique key.
|
|
54
63
|
*/
|
|
55
|
-
const { dds: mediaSession } = useDynamicDDS<LiveMediaSession>(
|
|
64
|
+
const { dds: mediaSession } = useDynamicDDS<LiveMediaSession>(
|
|
65
|
+
uniqueKey,
|
|
66
|
+
LiveMediaSession
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const { container } = useFluidObjectsContext();
|
|
56
70
|
|
|
57
71
|
/**
|
|
58
|
-
*
|
|
72
|
+
* Play callback
|
|
59
73
|
*/
|
|
60
|
-
const play = React.useCallback(()
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
const play = React.useCallback(async () => {
|
|
75
|
+
if (!container) {
|
|
76
|
+
throw new ActionContainerNotJoinedError(
|
|
77
|
+
"mediaSynchronizer",
|
|
78
|
+
"play"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (mediaSynchronizer === undefined) {
|
|
82
|
+
throw new ActionLiveDataObjectUndefinedError(
|
|
83
|
+
"mediaSynchronizer",
|
|
84
|
+
"play"
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return await mediaSynchronizer.play();
|
|
88
|
+
}, [container, mediaSynchronizer]);
|
|
63
89
|
|
|
64
90
|
/**
|
|
65
|
-
*
|
|
91
|
+
* Pause callback
|
|
66
92
|
*/
|
|
67
|
-
const pause = React.useCallback(()
|
|
68
|
-
|
|
69
|
-
|
|
93
|
+
const pause = React.useCallback(async () => {
|
|
94
|
+
if (!container) {
|
|
95
|
+
throw new ActionContainerNotJoinedError(
|
|
96
|
+
"mediaSynchronizer",
|
|
97
|
+
"pause"
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (mediaSynchronizer === undefined) {
|
|
101
|
+
throw new ActionLiveDataObjectUndefinedError(
|
|
102
|
+
"mediaSynchronizer",
|
|
103
|
+
"pause"
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return await mediaSynchronizer.pause();
|
|
107
|
+
}, [container, mediaSynchronizer]);
|
|
70
108
|
|
|
71
109
|
/**
|
|
72
110
|
* User facing: seek callback
|
|
73
111
|
*/
|
|
74
112
|
const seekTo = React.useCallback(
|
|
75
|
-
(time: number)
|
|
76
|
-
|
|
113
|
+
async (time: number) => {
|
|
114
|
+
if (!container) {
|
|
115
|
+
throw new ActionContainerNotJoinedError(
|
|
116
|
+
"mediaSynchronizer",
|
|
117
|
+
"seekTo"
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
if (mediaSynchronizer === undefined) {
|
|
121
|
+
throw new ActionLiveDataObjectUndefinedError(
|
|
122
|
+
"mediaSynchronizer",
|
|
123
|
+
"seekTo"
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return await mediaSynchronizer.seekTo(time);
|
|
77
127
|
},
|
|
78
|
-
[mediaSynchronizer]
|
|
128
|
+
[container, mediaSynchronizer]
|
|
79
129
|
);
|
|
80
130
|
|
|
81
131
|
/**
|
|
82
132
|
* User facing: set track callback
|
|
83
133
|
*/
|
|
84
134
|
const setTrack = React.useCallback(
|
|
85
|
-
(track: Partial<ExtendedMediaMetadata> | string | null)
|
|
86
|
-
|
|
135
|
+
async (track: Partial<ExtendedMediaMetadata> | string | null) => {
|
|
136
|
+
if (!container) {
|
|
137
|
+
throw new ActionContainerNotJoinedError(
|
|
138
|
+
"mediaSynchronizer",
|
|
139
|
+
"setTrack"
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (mediaSynchronizer === undefined) {
|
|
143
|
+
throw new ActionLiveDataObjectUndefinedError(
|
|
144
|
+
"mediaSynchronizer",
|
|
145
|
+
"setTrack"
|
|
146
|
+
);
|
|
147
|
+
}
|
|
87
148
|
if (isExtendedMediaMetadata(track)) {
|
|
88
|
-
mediaSynchronizer
|
|
149
|
+
return await mediaSynchronizer.setTrack(track);
|
|
89
150
|
} else if (typeof track === "string") {
|
|
90
|
-
mediaSynchronizer
|
|
151
|
+
return await mediaSynchronizer.setTrack({
|
|
91
152
|
trackIdentifier: track,
|
|
92
153
|
} as ExtendedMediaMetadata);
|
|
93
154
|
}
|
|
94
155
|
},
|
|
95
|
-
[mediaSynchronizer]
|
|
156
|
+
[container, mediaSynchronizer]
|
|
96
157
|
);
|
|
97
158
|
|
|
98
159
|
/**
|
|
@@ -119,18 +180,20 @@ export function useMediaSynchronizer(
|
|
|
119
180
|
* Setup change listeners and start `LiveMediaSession` if needed
|
|
120
181
|
*/
|
|
121
182
|
React.useEffect(() => {
|
|
122
|
-
if (mediaSession === undefined || !
|
|
123
|
-
|
|
183
|
+
if (mediaSession === undefined || !mediaPlayerElement) return;
|
|
184
|
+
let mounted = true;
|
|
124
185
|
// Query the HTML5 media element from the document and set reference
|
|
125
186
|
let mediaPlayer: IMediaPlayer | undefined;
|
|
126
|
-
if (isRefObject<IMediaPlayer>(
|
|
127
|
-
if (!
|
|
128
|
-
mediaPlayer =
|
|
129
|
-
} else {
|
|
130
|
-
const
|
|
131
|
-
|
|
187
|
+
if (isRefObject<IMediaPlayer>(mediaPlayerElement)) {
|
|
188
|
+
if (!mediaPlayerElement.current) return;
|
|
189
|
+
mediaPlayer = mediaPlayerElement.current;
|
|
190
|
+
} else if (typeof mediaPlayerElement === "string") {
|
|
191
|
+
const mediaElement = document.getElementById(
|
|
192
|
+
mediaPlayerElement
|
|
132
193
|
) as any;
|
|
133
|
-
if (!isMediaElement(
|
|
194
|
+
if (!isMediaElement(mediaElement)) return;
|
|
195
|
+
mediaPlayer = mediaElement;
|
|
196
|
+
} else {
|
|
134
197
|
mediaPlayer = mediaPlayerElement;
|
|
135
198
|
}
|
|
136
199
|
if (initialTrack) {
|
|
@@ -152,27 +215,31 @@ export function useMediaSynchronizer(
|
|
|
152
215
|
// Start synchronizing the media session
|
|
153
216
|
mediaSession.initialize(allowedRoles ?? []);
|
|
154
217
|
} else if (initialTrack) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
218
|
+
mediaSession.onLocalUserAllowed(async () => {
|
|
219
|
+
if (!mounted) return;
|
|
220
|
+
try {
|
|
221
|
+
if (isExtendedMediaMetadata(initialTrack)) {
|
|
222
|
+
await synchronizer.setTrack(initialTrack);
|
|
223
|
+
} else if (typeof initialTrack === "string") {
|
|
224
|
+
await synchronizer.setTrack({
|
|
225
|
+
trackIdentifier: initialTrack,
|
|
226
|
+
} as ExtendedMediaMetadata);
|
|
227
|
+
}
|
|
228
|
+
} catch (err) {
|
|
229
|
+
console.error(err);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
167
232
|
}
|
|
168
233
|
setMediaSynchronizer(synchronizer);
|
|
169
234
|
|
|
170
235
|
return () => {
|
|
236
|
+
mounted = false;
|
|
171
237
|
synchronizer.removeAllListeners();
|
|
172
238
|
mediaSession.removeAllListeners();
|
|
173
239
|
synchronizer?.end();
|
|
240
|
+
mediaSession?.dispose();
|
|
174
241
|
};
|
|
175
|
-
}, [mediaSession]);
|
|
242
|
+
}, [mediaSession, mediaPlayerElement]);
|
|
176
243
|
|
|
177
244
|
/**
|
|
178
245
|
* Change view in media synchronizer only if prop changes
|
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
* Licensed under the Microsoft Live Share SDK License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
IFluidContainer,
|
|
8
|
-
LoadableObjectClassRecord,
|
|
9
|
-
} from "fluid-framework";
|
|
6
|
+
import { IFluidContainer, LoadableObjectClassRecord } from "fluid-framework";
|
|
10
7
|
import React from "react";
|
|
11
8
|
import {
|
|
12
9
|
AzureClientProps,
|
|
@@ -16,7 +13,7 @@ import { IAzureContainerResults } from "../types";
|
|
|
16
13
|
import {
|
|
17
14
|
ISharedStateRegistryResponse,
|
|
18
15
|
useSharedStateRegistry,
|
|
19
|
-
} from "../internal
|
|
16
|
+
} from "../internal";
|
|
20
17
|
import {
|
|
21
18
|
AzureTurboClient,
|
|
22
19
|
IFluidTurboClient,
|
|
@@ -58,9 +55,7 @@ interface IAzureProviderProps {
|
|
|
58
55
|
/**
|
|
59
56
|
* React Context provider component for using Fluid data objects & joining/creating a Fluid document `AzureClient`.
|
|
60
57
|
*/
|
|
61
|
-
export const AzureProvider: React.FC<IAzureProviderProps> = (
|
|
62
|
-
props
|
|
63
|
-
) => {
|
|
58
|
+
export const AzureProvider: React.FC<IAzureProviderProps> = (props) => {
|
|
64
59
|
const startedRef = React.useRef<boolean>(false);
|
|
65
60
|
const clientRef = React.useRef<AzureTurboClient>(
|
|
66
61
|
new AzureTurboClient(props.clientOptions)
|
|
@@ -5,18 +5,44 @@
|
|
|
5
5
|
|
|
6
6
|
import { IFluidContainer, LoadableObjectClassRecord } from "fluid-framework";
|
|
7
7
|
import React from "react";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
8
|
+
import { useSharedStateRegistry } from "../internal";
|
|
9
|
+
import {
|
|
10
|
+
ILiveShareClientOptions,
|
|
11
|
+
ILiveShareHost,
|
|
12
|
+
ILiveShareJoinResults,
|
|
13
|
+
ITimestampProvider,
|
|
14
|
+
} from "@microsoft/live-share";
|
|
11
15
|
import { FluidContext } from "./AzureProvider";
|
|
12
16
|
import { LiveShareTurboClient } from "@microsoft/live-share-turbo";
|
|
13
17
|
|
|
14
|
-
interface ILiveShareContext {
|
|
15
|
-
|
|
18
|
+
export interface ILiveShareContext {
|
|
19
|
+
/**
|
|
20
|
+
* True if the local user created the Fluid container
|
|
21
|
+
*/
|
|
22
|
+
created: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* True if connected to the Live Share container
|
|
25
|
+
*/
|
|
26
|
+
joined: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* An error that will be defined if there was a problem joining the container, or undefined if not.
|
|
29
|
+
*/
|
|
30
|
+
joinError: Error | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Live Share timestamp provider. Can be used to `.getTimestamp()` for a global clock value.
|
|
33
|
+
* This reference timestamp value should be fairly consistent for all users in the session.
|
|
34
|
+
*/
|
|
35
|
+
timestampProvider: ITimestampProvider | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Join callback method
|
|
38
|
+
* @param initialObjects Optional. The initial objects for the Fluid container schema.
|
|
39
|
+
* @param onInitializeContainer Optional. Callback for when the container is first created.
|
|
40
|
+
* @returns `ILiveShareJoinResults`, which includes the Fluid container
|
|
41
|
+
*/
|
|
16
42
|
join: (
|
|
17
43
|
initialObjects?: LoadableObjectClassRecord,
|
|
18
44
|
onInitializeContainer?: (container: IFluidContainer) => void
|
|
19
|
-
) => Promise<
|
|
45
|
+
) => Promise<ILiveShareJoinResults>;
|
|
20
46
|
}
|
|
21
47
|
|
|
22
48
|
export const LiveShareContext = React.createContext<ILiveShareContext>(
|
|
@@ -54,15 +80,13 @@ interface ILiveShareProviderProps {
|
|
|
54
80
|
/**
|
|
55
81
|
* React Context provider component for using Live Share data objects & joining a Live Share session using `LiveShareClient`.
|
|
56
82
|
*/
|
|
57
|
-
export const LiveShareProvider: React.FC<
|
|
58
|
-
ILiveShareProviderProps
|
|
59
|
-
> = (props) => {
|
|
83
|
+
export const LiveShareProvider: React.FC<ILiveShareProviderProps> = (props) => {
|
|
60
84
|
const startedRef = React.useRef(false);
|
|
61
85
|
const clientRef = React.useRef(
|
|
62
86
|
new LiveShareTurboClient(props.host, props.clientOptions)
|
|
63
87
|
);
|
|
64
88
|
const [results, setResults] = React.useState<
|
|
65
|
-
|
|
89
|
+
ILiveShareJoinResults | undefined
|
|
66
90
|
>();
|
|
67
91
|
const [joinError, setJoinError] = React.useState<Error | undefined>();
|
|
68
92
|
|
|
@@ -75,10 +99,12 @@ export const LiveShareProvider: React.FC<
|
|
|
75
99
|
async (
|
|
76
100
|
initialObjects?: LoadableObjectClassRecord,
|
|
77
101
|
onInitializeContainer?: (container: IFluidContainer) => void
|
|
78
|
-
): Promise<
|
|
102
|
+
): Promise<ILiveShareJoinResults> => {
|
|
79
103
|
startedRef.current = true;
|
|
80
|
-
const results
|
|
81
|
-
|
|
104
|
+
const results = await clientRef.current.join(
|
|
105
|
+
initialObjects,
|
|
106
|
+
onInitializeContainer
|
|
107
|
+
);
|
|
82
108
|
setResults(results);
|
|
83
109
|
return results;
|
|
84
110
|
},
|
|
@@ -92,9 +118,10 @@ export const LiveShareProvider: React.FC<
|
|
|
92
118
|
// This hook should only be called once, so we use a ref to track if it has been called.
|
|
93
119
|
// This is a workaround for the fact that useEffect is called twice on initial render in React V18.
|
|
94
120
|
// We are not doing this here for backwards compatibility. View the README for more information.
|
|
95
|
-
if (results !== undefined || startedRef.current || !props.joinOnLoad)
|
|
121
|
+
if (results !== undefined || startedRef.current || !props.joinOnLoad)
|
|
122
|
+
return;
|
|
96
123
|
join(props.initialObjects).catch((error) => {
|
|
97
|
-
console.
|
|
124
|
+
console.error(error);
|
|
98
125
|
if (error instanceof Error) {
|
|
99
126
|
setJoinError(error);
|
|
100
127
|
} else {
|
|
@@ -110,7 +137,10 @@ export const LiveShareProvider: React.FC<
|
|
|
110
137
|
return (
|
|
111
138
|
<LiveShareContext.Provider
|
|
112
139
|
value={{
|
|
113
|
-
created: results?.created,
|
|
140
|
+
created: !!results?.created,
|
|
141
|
+
timestampProvider: results?.timestampProvider,
|
|
142
|
+
joined: !!results?.container,
|
|
143
|
+
joinError,
|
|
114
144
|
join,
|
|
115
145
|
}}
|
|
116
146
|
>
|