@microsoft/live-share-react 1.0.0-preview.5
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/.nycrc.json +10 -0
- package/LICENSE +87 -0
- package/README.md +543 -0
- package/bin/index.d.ts +7 -0
- package/bin/index.d.ts.map +1 -0
- package/bin/index.js +7 -0
- package/bin/index.js.map +1 -0
- package/bin/interfaces/LiveInterfaceExtensions.d.ts +9 -0
- package/bin/interfaces/LiveInterfaceExtensions.d.ts.map +1 -0
- package/bin/interfaces/LiveInterfaceExtensions.js +6 -0
- package/bin/interfaces/LiveInterfaceExtensions.js.map +1 -0
- package/bin/interfaces/index.d.ts +2 -0
- package/bin/interfaces/index.d.ts.map +1 -0
- package/bin/interfaces/index.js +2 -0
- package/bin/interfaces/index.js.map +1 -0
- package/bin/internal-hooks/index.d.ts +2 -0
- package/bin/internal-hooks/index.d.ts.map +1 -0
- package/bin/internal-hooks/index.js +2 -0
- package/bin/internal-hooks/index.js.map +1 -0
- package/bin/internal-hooks/useDynamicDDSRegistry.d.ts +14 -0
- package/bin/internal-hooks/useDynamicDDSRegistry.d.ts.map +1 -0
- package/bin/internal-hooks/useDynamicDDSRegistry.js +142 -0
- package/bin/internal-hooks/useDynamicDDSRegistry.js.map +1 -0
- package/bin/internal-hooks/useSharedStateRegistry.d.ts +31 -0
- package/bin/internal-hooks/useSharedStateRegistry.d.ts.map +1 -0
- package/bin/internal-hooks/useSharedStateRegistry.js +100 -0
- package/bin/internal-hooks/useSharedStateRegistry.js.map +1 -0
- package/bin/internal-hooks/useStateRef.d.ts +7 -0
- package/bin/internal-hooks/useStateRef.d.ts.map +1 -0
- package/bin/internal-hooks/useStateRef.js +15 -0
- package/bin/internal-hooks/useStateRef.js.map +1 -0
- package/bin/live-hooks/index.d.ts +7 -0
- package/bin/live-hooks/index.d.ts.map +1 -0
- package/bin/live-hooks/index.js +7 -0
- package/bin/live-hooks/index.js.map +1 -0
- package/bin/live-hooks/useLiveAICompletion.d.ts +29 -0
- package/bin/live-hooks/useLiveAICompletion.d.ts.map +1 -0
- package/bin/live-hooks/useLiveAICompletion.js +186 -0
- package/bin/live-hooks/useLiveAICompletion.js.map +1 -0
- package/bin/live-hooks/useLiveCanvas.d.ts +28 -0
- package/bin/live-hooks/useLiveCanvas.d.ts.map +1 -0
- package/bin/live-hooks/useLiveCanvas.js +167 -0
- package/bin/live-hooks/useLiveCanvas.js.map +1 -0
- package/bin/live-hooks/useLiveCoPilot.d.ts +28 -0
- package/bin/live-hooks/useLiveCoPilot.d.ts.map +1 -0
- package/bin/live-hooks/useLiveCoPilot.js +178 -0
- package/bin/live-hooks/useLiveCoPilot.js.map +1 -0
- package/bin/live-hooks/useLiveEvent.d.ts +22 -0
- package/bin/live-hooks/useLiveEvent.d.ts.map +1 -0
- package/bin/live-hooks/useLiveEvent.js +86 -0
- package/bin/live-hooks/useLiveEvent.js.map +1 -0
- package/bin/live-hooks/useLivePresence.d.ts +26 -0
- package/bin/live-hooks/useLivePresence.d.ts.map +1 -0
- package/bin/live-hooks/useLivePresence.js +93 -0
- package/bin/live-hooks/useLivePresence.js.map +1 -0
- package/bin/live-hooks/useLiveState.d.ts +20 -0
- package/bin/live-hooks/useLiveState.d.ts.map +1 -0
- package/bin/live-hooks/useLiveState.js +71 -0
- package/bin/live-hooks/useLiveState.js.map +1 -0
- package/bin/live-hooks/useLiveTimer.d.ts +8 -0
- package/bin/live-hooks/useLiveTimer.d.ts.map +1 -0
- package/bin/live-hooks/useLiveTimer.js +132 -0
- package/bin/live-hooks/useLiveTimer.js.map +1 -0
- package/bin/live-hooks/useMediaSynchronizer.d.ts +23 -0
- package/bin/live-hooks/useMediaSynchronizer.d.ts.map +1 -0
- package/bin/live-hooks/useMediaSynchronizer.js +162 -0
- package/bin/live-hooks/useMediaSynchronizer.js.map +1 -0
- package/bin/live-share-turbo/AzureTurboClient.d.ts +39 -0
- package/bin/live-share-turbo/AzureTurboClient.d.ts.map +1 -0
- package/bin/live-share-turbo/AzureTurboClient.js +56 -0
- package/bin/live-share-turbo/AzureTurboClient.js.map +1 -0
- package/bin/live-share-turbo/LiveShareTurboClient.d.ts +53 -0
- package/bin/live-share-turbo/LiveShareTurboClient.d.ts.map +1 -0
- package/bin/live-share-turbo/LiveShareTurboClient.js +69 -0
- package/bin/live-share-turbo/LiveShareTurboClient.js.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboDataObject.d.ts +31 -0
- package/bin/live-share-turbo/dds-objects/TurboDataObject.d.ts.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboDataObject.js +75 -0
- package/bin/live-share-turbo/dds-objects/TurboDataObject.js.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveCanvas.d.ts +48 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveCanvas.d.ts.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveCanvas.js +104 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveCanvas.js.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveEvent.d.ts +28 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveEvent.d.ts.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveEvent.js +68 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveEvent.js.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveMediaSession.d.ts +60 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveMediaSession.d.ts.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveMediaSession.js +111 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveMediaSession.js.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLivePresence.d.ts +77 -0
- package/bin/live-share-turbo/dds-objects/TurboLivePresence.d.ts.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLivePresence.js +137 -0
- package/bin/live-share-turbo/dds-objects/TurboLivePresence.js.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveState.d.ts +30 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveState.d.ts.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveState.js +74 -0
- package/bin/live-share-turbo/dds-objects/TurboLiveState.js.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboSharedMap.d.ts +61 -0
- package/bin/live-share-turbo/dds-objects/TurboSharedMap.d.ts.map +1 -0
- package/bin/live-share-turbo/dds-objects/TurboSharedMap.js +108 -0
- package/bin/live-share-turbo/dds-objects/TurboSharedMap.js.map +1 -0
- package/bin/live-share-turbo/dds-objects/index.d.ts +8 -0
- package/bin/live-share-turbo/dds-objects/index.d.ts.map +1 -0
- package/bin/live-share-turbo/dds-objects/index.js +8 -0
- package/bin/live-share-turbo/dds-objects/index.js.map +1 -0
- package/bin/live-share-turbo/index.d.ts +5 -0
- package/bin/live-share-turbo/index.d.ts.map +1 -0
- package/bin/live-share-turbo/index.js +5 -0
- package/bin/live-share-turbo/index.js.map +1 -0
- package/bin/live-share-turbo/interfaces/IFluidTurboClient.d.ts +15 -0
- package/bin/live-share-turbo/interfaces/IFluidTurboClient.d.ts.map +1 -0
- package/bin/live-share-turbo/interfaces/IFluidTurboClient.js +2 -0
- package/bin/live-share-turbo/interfaces/IFluidTurboClient.js.map +1 -0
- package/bin/live-share-turbo/interfaces/index.d.ts +3 -0
- package/bin/live-share-turbo/interfaces/index.d.ts.map +1 -0
- package/bin/live-share-turbo/interfaces/index.js +3 -0
- package/bin/live-share-turbo/interfaces/index.js.map +1 -0
- package/bin/live-share-turbo/internals/FluidTurboClient.d.ts +39 -0
- package/bin/live-share-turbo/internals/FluidTurboClient.d.ts.map +1 -0
- package/bin/live-share-turbo/internals/FluidTurboClient.js +121 -0
- package/bin/live-share-turbo/internals/FluidTurboClient.js.map +1 -0
- package/bin/live-share-turbo/internals/index.d.ts +2 -0
- package/bin/live-share-turbo/internals/index.d.ts.map +1 -0
- package/bin/live-share-turbo/internals/index.js +2 -0
- package/bin/live-share-turbo/internals/index.js.map +1 -0
- package/bin/live-share-turbo/internals/types.d.ts +4 -0
- package/bin/live-share-turbo/internals/types.d.ts.map +1 -0
- package/bin/live-share-turbo/internals/types.js +2 -0
- package/bin/live-share-turbo/internals/types.js.map +1 -0
- package/bin/providers/AzureProvider.d.ts +34 -0
- package/bin/providers/AzureProvider.d.ts.map +1 -0
- package/bin/providers/AzureProvider.js +102 -0
- package/bin/providers/AzureProvider.js.map +1 -0
- package/bin/providers/FluidContextProvider.d.ts +34 -0
- package/bin/providers/FluidContextProvider.d.ts.map +1 -0
- package/bin/providers/FluidContextProvider.js +99 -0
- package/bin/providers/FluidContextProvider.js.map +1 -0
- package/bin/providers/LiveShareContextProvider.d.ts +42 -0
- package/bin/providers/LiveShareContextProvider.d.ts.map +1 -0
- package/bin/providers/LiveShareContextProvider.js +63 -0
- package/bin/providers/LiveShareContextProvider.js.map +1 -0
- package/bin/providers/LiveShareProvider.d.ts +42 -0
- package/bin/providers/LiveShareProvider.d.ts.map +1 -0
- package/bin/providers/LiveShareProvider.js +66 -0
- package/bin/providers/LiveShareProvider.js.map +1 -0
- package/bin/providers/index.d.ts +3 -0
- package/bin/providers/index.d.ts.map +1 -0
- package/bin/providers/index.js +3 -0
- package/bin/providers/index.js.map +1 -0
- package/bin/shared-hooks/index.d.ts +5 -0
- package/bin/shared-hooks/index.d.ts.map +1 -0
- package/bin/shared-hooks/index.js +5 -0
- package/bin/shared-hooks/index.js.map +1 -0
- package/bin/shared-hooks/useDynamicDDS.d.ts +19 -0
- package/bin/shared-hooks/useDynamicDDS.d.ts.map +1 -0
- package/bin/shared-hooks/useDynamicDDS.js +68 -0
- package/bin/shared-hooks/useDynamicDDS.js.map +1 -0
- package/bin/shared-hooks/useLockedTask.d.ts +11 -0
- package/bin/shared-hooks/useLockedTask.d.ts.map +1 -0
- package/bin/shared-hooks/useLockedTask.js +82 -0
- package/bin/shared-hooks/useLockedTask.js.map +1 -0
- package/bin/shared-hooks/useSharedMap.d.ts +22 -0
- package/bin/shared-hooks/useSharedMap.d.ts.map +1 -0
- package/bin/shared-hooks/useSharedMap.js +106 -0
- package/bin/shared-hooks/useSharedMap.js.map +1 -0
- package/bin/shared-hooks/useSharedState.d.ts +21 -0
- package/bin/shared-hooks/useSharedState.d.ts.map +1 -0
- package/bin/shared-hooks/useSharedState.js +63 -0
- package/bin/shared-hooks/useSharedState.js.map +1 -0
- package/bin/shared-hooks/useTaskManager.d.ts +13 -0
- package/bin/shared-hooks/useTaskManager.d.ts.map +1 -0
- package/bin/shared-hooks/useTaskManager.js +86 -0
- package/bin/shared-hooks/useTaskManager.js.map +1 -0
- package/bin/types/ActionTypes.d.ts +30 -0
- package/bin/types/ActionTypes.d.ts.map +1 -0
- package/bin/types/ActionTypes.js +6 -0
- package/bin/types/ActionTypes.js.map +1 -0
- package/bin/types/PropTypes.d.ts +8 -0
- package/bin/types/PropTypes.d.ts.map +1 -0
- package/bin/types/PropTypes.js +6 -0
- package/bin/types/PropTypes.js.map +1 -0
- package/bin/types/ResultTypes.d.ts +163 -0
- package/bin/types/ResultTypes.d.ts.map +1 -0
- package/bin/types/ResultTypes.js +6 -0
- package/bin/types/ResultTypes.js.map +1 -0
- package/bin/types/index.d.ts +4 -0
- package/bin/types/index.d.ts.map +1 -0
- package/bin/types/index.js +4 -0
- package/bin/types/index.js.map +1 -0
- package/bin/utils/getContainerSchema.d.ts +8 -0
- package/bin/utils/getContainerSchema.d.ts.map +1 -0
- package/bin/utils/getContainerSchema.js +41 -0
- package/bin/utils/getContainerSchema.js.map +1 -0
- package/bin/utils/index.d.ts +2 -0
- package/bin/utils/index.d.ts.map +1 -0
- package/bin/utils/index.js +2 -0
- package/bin/utils/index.js.map +1 -0
- package/bin/utils/type-check.d.ts +15 -0
- package/bin/utils/type-check.d.ts.map +1 -0
- package/bin/utils/type-check.js +54 -0
- package/bin/utils/type-check.js.map +1 -0
- package/package.json +47 -0
- package/src/index.ts +6 -0
- package/src/interfaces/LiveInterfaceExtensions.ts +9 -0
- package/src/interfaces/index.ts +1 -0
- package/src/internal-hooks/index.ts +1 -0
- package/src/internal-hooks/useSharedStateRegistry.ts +163 -0
- package/src/live-hooks/index.ts +6 -0
- package/src/live-hooks/useLiveCanvas.ts +198 -0
- package/src/live-hooks/useLiveEvent.ts +117 -0
- package/src/live-hooks/useLivePresence.ts +124 -0
- package/src/live-hooks/useLiveState.ts +108 -0
- package/src/live-hooks/useLiveTimer.ts +184 -0
- package/src/live-hooks/useMediaSynchronizer.ts +200 -0
- package/src/providers/AzureProvider.tsx +177 -0
- package/src/providers/LiveShareProvider.tsx +140 -0
- package/src/providers/index.ts +2 -0
- package/src/shared-hooks/index.ts +4 -0
- package/src/shared-hooks/useDynamicDDS.ts +71 -0
- package/src/shared-hooks/useSharedMap.ts +132 -0
- package/src/shared-hooks/useSharedState.ts +85 -0
- package/src/shared-hooks/useTaskManager.ts +82 -0
- package/src/types/ActionTypes.ts +76 -0
- package/src/types/PropTypes.ts +10 -0
- package/src/types/ResultTypes.ts +187 -0
- package/src/types/index.ts +3 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/type-check.ts +69 -0
- package/tinylicious.log +4 -0
- package/tsconfig.build.json +6 -0
- package/tsconfig.json +72 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the Microsoft Live Share SDK License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ITimerConfig,
|
|
8
|
+
LiveTimerEvents,
|
|
9
|
+
UserMeetingRole,
|
|
10
|
+
LiveTimer,
|
|
11
|
+
} from "@microsoft/live-share";
|
|
12
|
+
import React from "react";
|
|
13
|
+
import {
|
|
14
|
+
IUseLiveTimerResults,
|
|
15
|
+
OnPauseTimerAction,
|
|
16
|
+
OnPlayTimerAction,
|
|
17
|
+
OnStartTimerAction,
|
|
18
|
+
OnTimerDidFinishAction,
|
|
19
|
+
OnTimerDidPauseAction,
|
|
20
|
+
OnTimerDidPlayAction,
|
|
21
|
+
OnTimerDidStartAction,
|
|
22
|
+
OnTimerTickAction,
|
|
23
|
+
} from "../types";
|
|
24
|
+
import { useDynamicDDS } from "../shared-hooks";
|
|
25
|
+
|
|
26
|
+
export function useLiveTimer(
|
|
27
|
+
uniqueKey: string,
|
|
28
|
+
allowedRoles?: UserMeetingRole[],
|
|
29
|
+
tickRate?: number,
|
|
30
|
+
onTick?: OnTimerTickAction,
|
|
31
|
+
onStart?: OnTimerDidStartAction,
|
|
32
|
+
onPause?: OnTimerDidPauseAction,
|
|
33
|
+
onPlay?: OnTimerDidPlayAction,
|
|
34
|
+
onFinish?: OnTimerDidFinishAction,
|
|
35
|
+
): IUseLiveTimerResults {
|
|
36
|
+
/**
|
|
37
|
+
* User facing: Stateful timer config.
|
|
38
|
+
*/
|
|
39
|
+
const [timerConfig, setTimerConfig] = React.useState<ITimerConfig>();
|
|
40
|
+
/**
|
|
41
|
+
* User facing: Stateful time remaining.
|
|
42
|
+
*/
|
|
43
|
+
const [milliRemaining, setMilliRemaining] = React.useState<number>();
|
|
44
|
+
/**
|
|
45
|
+
* User facing: dynamically load the DDS for the given unique key.
|
|
46
|
+
*/
|
|
47
|
+
const { dds: liveTimer } = useDynamicDDS<LiveTimer>(uniqueKey, LiveTimer);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* User facing: callback to send event through `LiveTimer`
|
|
51
|
+
*/
|
|
52
|
+
const start: OnStartTimerAction = React.useCallback(
|
|
53
|
+
(duration: number): void => {
|
|
54
|
+
if (liveTimer?.isInitialized === undefined) {
|
|
55
|
+
console.error(
|
|
56
|
+
new Error("Cannot call start when liveTimer is undefined")
|
|
57
|
+
);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (!liveTimer.isInitialized) {
|
|
61
|
+
console.error(
|
|
62
|
+
new Error(
|
|
63
|
+
"Cannot call start while liveTimer is not started"
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
liveTimer?.start(duration);
|
|
69
|
+
},
|
|
70
|
+
[liveTimer?.isInitialized]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* User facing: callback to send event through `LiveTimer`
|
|
75
|
+
*/
|
|
76
|
+
const play: OnPlayTimerAction = React.useCallback((): void => {
|
|
77
|
+
if (liveTimer?.isInitialized === undefined) {
|
|
78
|
+
console.error(
|
|
79
|
+
new Error("Cannot call play when liveTimer is undefined")
|
|
80
|
+
);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!liveTimer.isInitialized) {
|
|
84
|
+
console.error(
|
|
85
|
+
new Error("Cannot call play while liveTimer is not started")
|
|
86
|
+
);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
liveTimer?.play();
|
|
90
|
+
}, [liveTimer?.isInitialized]);
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* User facing: callback to send event through `LiveTimer`
|
|
94
|
+
*/
|
|
95
|
+
const pause: OnPauseTimerAction = React.useCallback((): void => {
|
|
96
|
+
if (liveTimer?.isInitialized === undefined) {
|
|
97
|
+
console.error(
|
|
98
|
+
new Error("Cannot call pause when liveTimer is undefined")
|
|
99
|
+
);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!liveTimer.isInitialized) {
|
|
103
|
+
console.error(
|
|
104
|
+
new Error("Cannot call pause while liveTimer is not started")
|
|
105
|
+
);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
liveTimer?.pause();
|
|
109
|
+
}, [liveTimer?.isInitialized]);
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Setup change listeners and start `LiveTimer` if needed
|
|
113
|
+
*/
|
|
114
|
+
React.useEffect(() => {
|
|
115
|
+
if (liveTimer === undefined)
|
|
116
|
+
return;
|
|
117
|
+
// Register event listeners
|
|
118
|
+
const onTimerConfigChange = (config: ITimerConfig) => {
|
|
119
|
+
setTimerConfig(config);
|
|
120
|
+
};
|
|
121
|
+
const onDidStart = (config: ITimerConfig) => {
|
|
122
|
+
onTimerConfigChange(config);
|
|
123
|
+
onStart?.(config);
|
|
124
|
+
setMilliRemaining(config.duration);
|
|
125
|
+
};
|
|
126
|
+
const onDidFinish = (config: ITimerConfig) => {
|
|
127
|
+
onTimerConfigChange(config);
|
|
128
|
+
onFinish?.(config);
|
|
129
|
+
setMilliRemaining(0);
|
|
130
|
+
};
|
|
131
|
+
const onDidPlay = (config: ITimerConfig) => {
|
|
132
|
+
onTimerConfigChange(config);
|
|
133
|
+
onPlay?.(config);
|
|
134
|
+
};
|
|
135
|
+
const onDidPause = (config: ITimerConfig) => {
|
|
136
|
+
onTimerConfigChange(config);
|
|
137
|
+
onPause?.(config);
|
|
138
|
+
};
|
|
139
|
+
const onDidTick = (milliseconds: number) => {
|
|
140
|
+
setMilliRemaining(milliseconds);
|
|
141
|
+
onTick?.(milliseconds);
|
|
142
|
+
};
|
|
143
|
+
liveTimer.on(LiveTimerEvents.started, onDidStart);
|
|
144
|
+
liveTimer.on(LiveTimerEvents.finished, onDidFinish);
|
|
145
|
+
liveTimer.on(LiveTimerEvents.played, onDidPlay);
|
|
146
|
+
liveTimer.on(LiveTimerEvents.paused, onDidPause);
|
|
147
|
+
liveTimer.on(LiveTimerEvents.onTick, onDidTick);
|
|
148
|
+
if (!liveTimer.isInitialized) {
|
|
149
|
+
// Start live event
|
|
150
|
+
liveTimer.initialize(allowedRoles);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return () => {
|
|
154
|
+
// on unmount, remove event listeners
|
|
155
|
+
liveTimer?.off(LiveTimerEvents.started, onDidStart);
|
|
156
|
+
liveTimer?.off(LiveTimerEvents.finished, onDidFinish);
|
|
157
|
+
liveTimer?.off(LiveTimerEvents.played, onDidPlay);
|
|
158
|
+
liveTimer?.off(LiveTimerEvents.paused, onDidPause);
|
|
159
|
+
liveTimer?.off(LiveTimerEvents.onTick, onDidTick);
|
|
160
|
+
};
|
|
161
|
+
}, [liveTimer, onTick, onStart, onFinish, onPlay, onPause]);
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Change tick rate if changes in props
|
|
165
|
+
*/
|
|
166
|
+
React.useEffect(() => {
|
|
167
|
+
if (
|
|
168
|
+
tickRate !== undefined &&
|
|
169
|
+
liveTimer?.tickRate !== undefined &&
|
|
170
|
+
tickRate !== liveTimer?.tickRate
|
|
171
|
+
) {
|
|
172
|
+
liveTimer.tickRate = tickRate;
|
|
173
|
+
}
|
|
174
|
+
}, [tickRate, liveTimer?.tickRate]);
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
liveTimer,
|
|
178
|
+
milliRemaining,
|
|
179
|
+
pause,
|
|
180
|
+
play,
|
|
181
|
+
start,
|
|
182
|
+
timerConfig,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the Microsoft Live Share SDK License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { UserMeetingRole } from "@microsoft/live-share";
|
|
7
|
+
import React from "react";
|
|
8
|
+
import {
|
|
9
|
+
CoordinationWaitPoint,
|
|
10
|
+
ExtendedMediaMetadata,
|
|
11
|
+
IMediaPlayer,
|
|
12
|
+
LiveMediaSession,
|
|
13
|
+
MediaPlayerSynchronizer,
|
|
14
|
+
MediaSessionCoordinatorSuspension,
|
|
15
|
+
} from "@microsoft/live-share-media";
|
|
16
|
+
import { isExtendedMediaMetadata, isMediaElement, isRefObject } from "../utils";
|
|
17
|
+
import { useDynamicDDS } from "../shared-hooks";
|
|
18
|
+
import { IUseMediaSynchronizerResults } from "../types";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* React hook for using a Live Share media `MediaPlayerSynchronizer`.
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* Use this hook if you want to synchronize the playback position of a video or audio element during a Live Share session.
|
|
25
|
+
*
|
|
26
|
+
* @param uniqueKey uniqueKey value for the `LiveMediaSession` DDS object used by `MediaPlayerSynchronizer`.
|
|
27
|
+
* @param mediaPlayerElementRef React RefObject containing object/element conforming to IMediaPlayer interface or string id for <video> / <audio> element.
|
|
28
|
+
* @param initialTrack initial track to load. Either ExtendedMediaMetadata, trackId string, or null.
|
|
29
|
+
* @param allowedRoles Optional. Array of user roles that are eligible to modify group playback state.
|
|
30
|
+
* @param viewOnly Optional. Flag for whether or not the media synchronizer should be in viewOnly mode.
|
|
31
|
+
* @returns IUseMediaSynchronizerResults object.
|
|
32
|
+
*/
|
|
33
|
+
export function useMediaSynchronizer(
|
|
34
|
+
uniqueKey: string,
|
|
35
|
+
mediaPlayerElementRef: React.RefObject<IMediaPlayer> | string,
|
|
36
|
+
initialTrack: Partial<ExtendedMediaMetadata> | string | null,
|
|
37
|
+
allowedRoles?: UserMeetingRole[],
|
|
38
|
+
viewOnly?: boolean
|
|
39
|
+
): IUseMediaSynchronizerResults {
|
|
40
|
+
/**
|
|
41
|
+
* User facing media synchronizer and non-user facing setter
|
|
42
|
+
*/
|
|
43
|
+
const [mediaSynchronizer, setMediaSynchronizer] = React.useState<
|
|
44
|
+
MediaPlayerSynchronizer | undefined
|
|
45
|
+
>();
|
|
46
|
+
/**
|
|
47
|
+
* Suspension object and setter
|
|
48
|
+
*/
|
|
49
|
+
const [suspension, setSuspension] = React.useState<
|
|
50
|
+
MediaSessionCoordinatorSuspension | undefined
|
|
51
|
+
>();
|
|
52
|
+
/**
|
|
53
|
+
* User facing: dynamically load the LiveMediaSession DDS for the given unique key.
|
|
54
|
+
*/
|
|
55
|
+
const { dds: mediaSession } = useDynamicDDS<LiveMediaSession>(uniqueKey, LiveMediaSession);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* User facing: play callback
|
|
59
|
+
*/
|
|
60
|
+
const play = React.useCallback((): void => {
|
|
61
|
+
mediaSynchronizer?.play();
|
|
62
|
+
}, [mediaSynchronizer]);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* User facing: pause callback
|
|
66
|
+
*/
|
|
67
|
+
const pause = React.useCallback((): void => {
|
|
68
|
+
mediaSynchronizer?.pause();
|
|
69
|
+
}, [mediaSynchronizer]);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* User facing: seek callback
|
|
73
|
+
*/
|
|
74
|
+
const seekTo = React.useCallback(
|
|
75
|
+
(time: number): void => {
|
|
76
|
+
mediaSynchronizer?.seekTo(time);
|
|
77
|
+
},
|
|
78
|
+
[mediaSynchronizer]
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* User facing: set track callback
|
|
83
|
+
*/
|
|
84
|
+
const setTrack = React.useCallback(
|
|
85
|
+
(track: Partial<ExtendedMediaMetadata> | string | null): void => {
|
|
86
|
+
// TODO: fix force unwrap once synchronizer uses correct types
|
|
87
|
+
if (isExtendedMediaMetadata(track)) {
|
|
88
|
+
mediaSynchronizer!.setTrack(track);
|
|
89
|
+
} else if (typeof track === "string") {
|
|
90
|
+
mediaSynchronizer!.setTrack({
|
|
91
|
+
trackIdentifier: track,
|
|
92
|
+
} as ExtendedMediaMetadata);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
[mediaSynchronizer]
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* User facing: create a new suspension and set it locally to state
|
|
100
|
+
*/
|
|
101
|
+
const beginSuspension = React.useCallback(
|
|
102
|
+
(waitPoint?: CoordinationWaitPoint) => {
|
|
103
|
+
const suspension =
|
|
104
|
+
mediaSession?.coordinator.beginSuspension(waitPoint);
|
|
105
|
+
setSuspension(suspension);
|
|
106
|
+
},
|
|
107
|
+
[mediaSession]
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* User facing: if a suspension is active, end it. Called when "Follow presenter" button is clicked.
|
|
112
|
+
*/
|
|
113
|
+
const endSuspension = React.useCallback(() => {
|
|
114
|
+
suspension?.end();
|
|
115
|
+
setSuspension(undefined);
|
|
116
|
+
}, [suspension]);
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Setup change listeners and start `LiveMediaSession` if needed
|
|
120
|
+
*/
|
|
121
|
+
React.useEffect(() => {
|
|
122
|
+
if (mediaSession === undefined || !mediaPlayerElementRef)
|
|
123
|
+
return;
|
|
124
|
+
// Query the HTML5 media element from the document and set reference
|
|
125
|
+
let mediaPlayer: IMediaPlayer | undefined;
|
|
126
|
+
if (isRefObject<IMediaPlayer>(mediaPlayerElementRef)) {
|
|
127
|
+
if (!mediaPlayerElementRef.current) return;
|
|
128
|
+
mediaPlayer = mediaPlayerElementRef.current;
|
|
129
|
+
} else {
|
|
130
|
+
const mediaPlayerElement = document.getElementById(
|
|
131
|
+
mediaPlayerElementRef
|
|
132
|
+
) as any;
|
|
133
|
+
if (!isMediaElement(mediaPlayerElement)) return;
|
|
134
|
+
mediaPlayer = mediaPlayerElement;
|
|
135
|
+
}
|
|
136
|
+
if (initialTrack) {
|
|
137
|
+
// If supported type and not null, set the initial src
|
|
138
|
+
if (isExtendedMediaMetadata(initialTrack)) {
|
|
139
|
+
mediaPlayer.src = initialTrack.trackIdentifier;
|
|
140
|
+
} else if (typeof initialTrack === "string") {
|
|
141
|
+
mediaPlayer.src = initialTrack;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Begin synchronizing a MediaSynchronizer for the player
|
|
145
|
+
const synchronizer = mediaSession.synchronize(mediaPlayer);
|
|
146
|
+
if (viewOnly !== undefined) {
|
|
147
|
+
// Set synchronizer to view only mode provided by developer
|
|
148
|
+
synchronizer.viewOnly = viewOnly;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!mediaSession.isInitialized) {
|
|
152
|
+
// Start synchronizing the media session
|
|
153
|
+
mediaSession.initialize(allowedRoles ?? []);
|
|
154
|
+
} else if (initialTrack) {
|
|
155
|
+
// If we have already started the media session, the synchronizer won't have initial track
|
|
156
|
+
// data now that we are remounting.
|
|
157
|
+
// TODO: if player is loaded twice in one app at once, then this has a chance to disrupt the
|
|
158
|
+
// group state by resetting the track. MediaPlayerSynchronizer does not currently have a way to
|
|
159
|
+
// get the current track, so to fix this we need a better solution.
|
|
160
|
+
if (isExtendedMediaMetadata(initialTrack)) {
|
|
161
|
+
synchronizer.setTrack(initialTrack);
|
|
162
|
+
} else if (typeof initialTrack === "string") {
|
|
163
|
+
synchronizer.setTrack({
|
|
164
|
+
trackIdentifier: initialTrack,
|
|
165
|
+
} as ExtendedMediaMetadata);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
setMediaSynchronizer(synchronizer);
|
|
169
|
+
|
|
170
|
+
return () => {
|
|
171
|
+
synchronizer.removeAllListeners();
|
|
172
|
+
mediaSession.removeAllListeners();
|
|
173
|
+
synchronizer?.end();
|
|
174
|
+
};
|
|
175
|
+
}, [mediaSession]);
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Change view in media synchronizer only if prop changes
|
|
179
|
+
*/
|
|
180
|
+
React.useEffect(() => {
|
|
181
|
+
if (
|
|
182
|
+
mediaSynchronizer?.viewOnly !== undefined &&
|
|
183
|
+
viewOnly !== undefined &&
|
|
184
|
+
mediaSynchronizer.viewOnly !== viewOnly
|
|
185
|
+
) {
|
|
186
|
+
mediaSynchronizer.viewOnly = !!viewOnly;
|
|
187
|
+
}
|
|
188
|
+
}, [viewOnly, mediaSynchronizer?.viewOnly]);
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
suspended: !!suspension,
|
|
192
|
+
mediaSynchronizer,
|
|
193
|
+
play,
|
|
194
|
+
pause,
|
|
195
|
+
seekTo,
|
|
196
|
+
setTrack,
|
|
197
|
+
beginSuspension,
|
|
198
|
+
endSuspension,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the Microsoft Live Share SDK License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
IFluidContainer,
|
|
8
|
+
LoadableObjectClassRecord,
|
|
9
|
+
} from "fluid-framework";
|
|
10
|
+
import React from "react";
|
|
11
|
+
import {
|
|
12
|
+
AzureClientProps,
|
|
13
|
+
AzureContainerServices,
|
|
14
|
+
} from "@fluidframework/azure-client";
|
|
15
|
+
import { IAzureContainerResults } from "../types";
|
|
16
|
+
import {
|
|
17
|
+
ISharedStateRegistryResponse,
|
|
18
|
+
useSharedStateRegistry,
|
|
19
|
+
} from "../internal-hooks";
|
|
20
|
+
import {
|
|
21
|
+
AzureTurboClient,
|
|
22
|
+
IFluidTurboClient,
|
|
23
|
+
} from "@microsoft/live-share-turbo";
|
|
24
|
+
|
|
25
|
+
interface IFluidContext extends ISharedStateRegistryResponse {
|
|
26
|
+
clientRef: React.MutableRefObject<IFluidTurboClient>;
|
|
27
|
+
container: IFluidContainer | undefined;
|
|
28
|
+
services: AzureContainerServices | undefined;
|
|
29
|
+
joinError: Error | undefined;
|
|
30
|
+
getContainer: (
|
|
31
|
+
containerId: string,
|
|
32
|
+
initialObjects?: LoadableObjectClassRecord
|
|
33
|
+
) => Promise<IAzureContainerResults>;
|
|
34
|
+
createContainer: (
|
|
35
|
+
initialObjects?: LoadableObjectClassRecord,
|
|
36
|
+
onInitializeContainer?: (container: IFluidContainer) => void
|
|
37
|
+
) => Promise<IAzureContainerResults>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const FluidContext = React.createContext<IFluidContext>(
|
|
41
|
+
{} as IFluidContext
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export const useFluidObjectsContext = (): IFluidContext => {
|
|
45
|
+
const context = React.useContext(FluidContext);
|
|
46
|
+
return context;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
interface IAzureProviderProps {
|
|
50
|
+
children?: React.ReactNode;
|
|
51
|
+
clientOptions: AzureClientProps;
|
|
52
|
+
containerId?: string;
|
|
53
|
+
createOnLoad?: boolean;
|
|
54
|
+
initialObjects?: LoadableObjectClassRecord;
|
|
55
|
+
joinOnLoad?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* React Context provider component for using Fluid data objects & joining/creating a Fluid document `AzureClient`.
|
|
60
|
+
*/
|
|
61
|
+
export const AzureProvider: React.FC<IAzureProviderProps> = (
|
|
62
|
+
props
|
|
63
|
+
) => {
|
|
64
|
+
const startedRef = React.useRef<boolean>(false);
|
|
65
|
+
const clientRef = React.useRef<AzureTurboClient>(
|
|
66
|
+
new AzureTurboClient(props.clientOptions)
|
|
67
|
+
);
|
|
68
|
+
const [results, setResults] = React.useState<
|
|
69
|
+
IAzureContainerResults | undefined
|
|
70
|
+
>();
|
|
71
|
+
const [joinError, setJoinError] = React.useState<Error | undefined>();
|
|
72
|
+
|
|
73
|
+
const stateRegistryCallbacks = useSharedStateRegistry(results);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the container for a given containerId using AzureClient
|
|
77
|
+
*/
|
|
78
|
+
const getContainer = React.useCallback(
|
|
79
|
+
async (
|
|
80
|
+
containerId: string,
|
|
81
|
+
initialObjects?: LoadableObjectClassRecord
|
|
82
|
+
): Promise<IAzureContainerResults> => {
|
|
83
|
+
return new Promise(async (resolve, reject) => {
|
|
84
|
+
try {
|
|
85
|
+
const results: IAzureContainerResults =
|
|
86
|
+
await clientRef.current.getContainer(
|
|
87
|
+
containerId,
|
|
88
|
+
initialObjects
|
|
89
|
+
);
|
|
90
|
+
setResults(results);
|
|
91
|
+
resolve(results);
|
|
92
|
+
} catch (error: any) {
|
|
93
|
+
if (error instanceof Error) {
|
|
94
|
+
setJoinError(error);
|
|
95
|
+
}
|
|
96
|
+
reject(error);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
[props.containerId, setResults]
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create container callback to create a new container using AzureClient
|
|
105
|
+
*/
|
|
106
|
+
const createContainer = React.useCallback(
|
|
107
|
+
async (
|
|
108
|
+
initialObjects?: LoadableObjectClassRecord,
|
|
109
|
+
onInitializeContainer?: (container: IFluidContainer) => void
|
|
110
|
+
): Promise<IAzureContainerResults> => {
|
|
111
|
+
return new Promise(async (resolve, reject) => {
|
|
112
|
+
try {
|
|
113
|
+
const results: IAzureContainerResults =
|
|
114
|
+
await clientRef.current.createContainer(initialObjects);
|
|
115
|
+
if (onInitializeContainer) {
|
|
116
|
+
onInitializeContainer(results.container);
|
|
117
|
+
}
|
|
118
|
+
const containerId = await results.container.attach();
|
|
119
|
+
window.location.hash = containerId;
|
|
120
|
+
setResults(results);
|
|
121
|
+
resolve(results);
|
|
122
|
+
} catch (error: any) {
|
|
123
|
+
if (error instanceof Error) {
|
|
124
|
+
setJoinError(error);
|
|
125
|
+
}
|
|
126
|
+
reject(error);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
[props.containerId, setResults]
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Joins a container if `props.joinOnLoad` is true and `props.containerId` is known. Creates a new container if `props.createOnLoad` is true
|
|
135
|
+
* and `props.containerId` is not known.
|
|
136
|
+
*/
|
|
137
|
+
React.useEffect(() => {
|
|
138
|
+
// This hook should only be called once, so we use a ref to track if it has been called.
|
|
139
|
+
// This is a workaround for the fact that useEffect is called twice on initial render in React V18.
|
|
140
|
+
// We are not doing this here for backwards compatibility. View the README for more information.
|
|
141
|
+
if (
|
|
142
|
+
results?.container?.connectionState !== undefined ||
|
|
143
|
+
startedRef.current
|
|
144
|
+
)
|
|
145
|
+
return;
|
|
146
|
+
startedRef.current = true;
|
|
147
|
+
if (props.containerId && props.joinOnLoad) {
|
|
148
|
+
getContainer(props.containerId, props.initialObjects);
|
|
149
|
+
} else if (!props.containerId && props.createOnLoad) {
|
|
150
|
+
createContainer(props.initialObjects);
|
|
151
|
+
}
|
|
152
|
+
}, [
|
|
153
|
+
results?.container?.connectionState,
|
|
154
|
+
props.containerId,
|
|
155
|
+
props.createOnLoad,
|
|
156
|
+
props.joinOnLoad,
|
|
157
|
+
props.initialObjects,
|
|
158
|
+
getContainer,
|
|
159
|
+
createContainer,
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<FluidContext.Provider
|
|
164
|
+
value={{
|
|
165
|
+
clientRef,
|
|
166
|
+
container: results?.container,
|
|
167
|
+
services: results?.services,
|
|
168
|
+
joinError,
|
|
169
|
+
createContainer,
|
|
170
|
+
getContainer,
|
|
171
|
+
...stateRegistryCallbacks,
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
{props.children}
|
|
175
|
+
</FluidContext.Provider>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the Microsoft Live Share SDK License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IFluidContainer, LoadableObjectClassRecord } from "fluid-framework";
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { ILiveShareContainerResults } from "../types";
|
|
9
|
+
import { useSharedStateRegistry } from "../internal-hooks";
|
|
10
|
+
import { ILiveShareClientOptions, ILiveShareHost } from "@microsoft/live-share";
|
|
11
|
+
import { FluidContext } from "./AzureProvider";
|
|
12
|
+
import { LiveShareTurboClient } from "@microsoft/live-share-turbo";
|
|
13
|
+
|
|
14
|
+
interface ILiveShareContext {
|
|
15
|
+
created: boolean | undefined;
|
|
16
|
+
join: (
|
|
17
|
+
initialObjects?: LoadableObjectClassRecord,
|
|
18
|
+
onInitializeContainer?: (container: IFluidContainer) => void
|
|
19
|
+
) => Promise<ILiveShareContainerResults>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const LiveShareContext = React.createContext<ILiveShareContext>(
|
|
23
|
+
{} as ILiveShareContext
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export const useLiveShareContext = (): ILiveShareContext => {
|
|
27
|
+
const context = React.useContext(LiveShareContext);
|
|
28
|
+
return context;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
interface ILiveShareProviderProps {
|
|
32
|
+
/**
|
|
33
|
+
* Optional. React children node for the React Context Provider
|
|
34
|
+
*/
|
|
35
|
+
children?: React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Optional. Options to pass into LiveShareClient initializer
|
|
38
|
+
*/
|
|
39
|
+
clientOptions?: ILiveShareClientOptions;
|
|
40
|
+
/**
|
|
41
|
+
* Host to initialize LiveShareClient with
|
|
42
|
+
*/
|
|
43
|
+
host: ILiveShareHost;
|
|
44
|
+
/**
|
|
45
|
+
* Optional. Initial Fluid objects to load when the container is first created
|
|
46
|
+
*/
|
|
47
|
+
initialObjects?: LoadableObjectClassRecord;
|
|
48
|
+
/**
|
|
49
|
+
* Optional. Flag to determine whether to join Fluid container on load
|
|
50
|
+
*/
|
|
51
|
+
joinOnLoad?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* React Context provider component for using Live Share data objects & joining a Live Share session using `LiveShareClient`.
|
|
56
|
+
*/
|
|
57
|
+
export const LiveShareProvider: React.FC<
|
|
58
|
+
ILiveShareProviderProps
|
|
59
|
+
> = (props) => {
|
|
60
|
+
const startedRef = React.useRef(false);
|
|
61
|
+
const clientRef = React.useRef(
|
|
62
|
+
new LiveShareTurboClient(props.host, props.clientOptions)
|
|
63
|
+
);
|
|
64
|
+
const [results, setResults] = React.useState<
|
|
65
|
+
ILiveShareContainerResults | undefined
|
|
66
|
+
>();
|
|
67
|
+
const [joinError, setJoinError] = React.useState<Error | undefined>();
|
|
68
|
+
|
|
69
|
+
const stateRegistryCallbacks = useSharedStateRegistry(results);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Join container callback for joining the Live Share session
|
|
73
|
+
*/
|
|
74
|
+
const join = React.useCallback(
|
|
75
|
+
async (
|
|
76
|
+
initialObjects?: LoadableObjectClassRecord,
|
|
77
|
+
onInitializeContainer?: (container: IFluidContainer) => void
|
|
78
|
+
): Promise<ILiveShareContainerResults> => {
|
|
79
|
+
startedRef.current = true;
|
|
80
|
+
const results: ILiveShareContainerResults =
|
|
81
|
+
await clientRef.current.join(initialObjects, onInitializeContainer);
|
|
82
|
+
setResults(results);
|
|
83
|
+
return results;
|
|
84
|
+
},
|
|
85
|
+
[]
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Joins the container on load if `props.joinOnLoad` is true
|
|
90
|
+
*/
|
|
91
|
+
React.useEffect(() => {
|
|
92
|
+
// This hook should only be called once, so we use a ref to track if it has been called.
|
|
93
|
+
// This is a workaround for the fact that useEffect is called twice on initial render in React V18.
|
|
94
|
+
// We are not doing this here for backwards compatibility. View the README for more information.
|
|
95
|
+
if (results !== undefined || startedRef.current || !props.joinOnLoad) return;
|
|
96
|
+
join(props.initialObjects).catch((error) => {
|
|
97
|
+
console.log(error);
|
|
98
|
+
if (error instanceof Error) {
|
|
99
|
+
setJoinError(error);
|
|
100
|
+
} else {
|
|
101
|
+
setJoinError(
|
|
102
|
+
new Error(
|
|
103
|
+
"LiveShareProvider: An unknown error occurred while joining container."
|
|
104
|
+
)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}, [results, props.joinOnLoad, props.initialObjects, join]);
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<LiveShareContext.Provider
|
|
112
|
+
value={{
|
|
113
|
+
created: results?.created,
|
|
114
|
+
join,
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
<FluidContext.Provider
|
|
118
|
+
value={{
|
|
119
|
+
clientRef,
|
|
120
|
+
container: results?.container,
|
|
121
|
+
services: results?.services,
|
|
122
|
+
joinError,
|
|
123
|
+
getContainer: async () => {
|
|
124
|
+
throw new Error(
|
|
125
|
+
"Cannot join new container through getContainer in LiveShareProvider"
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
createContainer: async () => {
|
|
129
|
+
throw new Error(
|
|
130
|
+
"Cannot create new container through createContainer in LiveShareProvider"
|
|
131
|
+
);
|
|
132
|
+
},
|
|
133
|
+
...stateRegistryCallbacks,
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
{props.children}
|
|
137
|
+
</FluidContext.Provider>
|
|
138
|
+
</LiveShareContext.Provider>
|
|
139
|
+
);
|
|
140
|
+
};
|