@stream-io/video-react-sdk 1.17.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/css/styles.css +16 -0
- package/dist/css/styles.css.map +1 -1
- package/dist/index.cjs.js +255 -56
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +255 -57
- package/dist/index.es.js.map +1 -1
- package/dist/src/core/components/CallLayout/LivestreamLayout.d.ts +14 -0
- package/dist/src/hooks/useEffectEvent.d.ts +1 -0
- package/dist/src/translations/index.d.ts +4 -0
- package/dist/src/wrappers/LivestreamPlayer/LivestreamPlayer.d.ts +21 -1
- package/package.json +4 -4
- package/src/core/components/CallLayout/LivestreamLayout.tsx +48 -6
- package/src/hooks/useEffectEvent.ts +16 -0
- package/src/hooks/usePersistedDevicePreferences.ts +181 -72
- package/src/translations/en.json +4 -0
- package/src/wrappers/LivestreamPlayer/LivestreamPlayer.tsx +119 -8
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import { Call } from '@stream-io/video-client';
|
|
3
|
-
import { useStreamVideoClient } from '@stream-io/video-react-bindings';
|
|
2
|
+
import { Call, CallingState } from '@stream-io/video-client';
|
|
4
3
|
import {
|
|
4
|
+
useCall,
|
|
5
|
+
useCallStateHooks,
|
|
6
|
+
useStreamVideoClient,
|
|
7
|
+
} from '@stream-io/video-react-bindings';
|
|
8
|
+
import {
|
|
9
|
+
BackstageLayout,
|
|
10
|
+
BackstageLayoutProps,
|
|
5
11
|
LivestreamLayout,
|
|
6
12
|
LivestreamLayoutProps,
|
|
7
13
|
StreamCall,
|
|
8
14
|
} from '../../core';
|
|
15
|
+
import { useEffectEvent } from '../../hooks/useEffectEvent';
|
|
9
16
|
|
|
10
17
|
export type LivestreamPlayerProps = {
|
|
11
18
|
/**
|
|
@@ -16,22 +23,45 @@ export type LivestreamPlayerProps = {
|
|
|
16
23
|
* The call ID.
|
|
17
24
|
*/
|
|
18
25
|
callId: string;
|
|
26
|
+
/**
|
|
27
|
+
* Determines when the viewer joins the call.
|
|
28
|
+
*
|
|
29
|
+
* `"asap"` behavior means joining the call as soon as it is possible
|
|
30
|
+
* (either the `join_ahead_time_seconds` setting allows it, or the user
|
|
31
|
+
* has a the capability to join backstage).
|
|
32
|
+
*
|
|
33
|
+
* `"live"` behavior means joining the call when it goes live.
|
|
34
|
+
*
|
|
35
|
+
* @default "asap"
|
|
36
|
+
*/
|
|
37
|
+
joinBehavior?: 'asap' | 'live';
|
|
19
38
|
/**
|
|
20
39
|
* The props for the {@link LivestreamLayout} component.
|
|
21
40
|
*/
|
|
22
41
|
layoutProps?: LivestreamLayoutProps;
|
|
42
|
+
/**
|
|
43
|
+
* The props for the {@link BackstageLayout} component.
|
|
44
|
+
*/
|
|
45
|
+
backstageProps?: BackstageLayoutProps;
|
|
46
|
+
/**
|
|
47
|
+
* Callback to handle errors while fetching or joining livestream.
|
|
48
|
+
*/
|
|
49
|
+
onError?: (error: any) => void;
|
|
23
50
|
};
|
|
24
51
|
|
|
25
52
|
export const LivestreamPlayer = (props: LivestreamPlayerProps) => {
|
|
26
|
-
const { callType, callId,
|
|
53
|
+
const { callType, callId, ...restProps } = props;
|
|
27
54
|
const client = useStreamVideoClient();
|
|
28
55
|
const [call, setCall] = useState<Call>();
|
|
56
|
+
const onError = useEffectEvent(props.onError);
|
|
57
|
+
|
|
29
58
|
useEffect(() => {
|
|
30
59
|
if (!client) return;
|
|
31
60
|
const myCall = client.call(callType, callId);
|
|
32
61
|
setCall(myCall);
|
|
33
|
-
myCall.
|
|
34
|
-
console.error('Failed to
|
|
62
|
+
myCall.get().catch((e) => {
|
|
63
|
+
console.error('Failed to fetch call', e);
|
|
64
|
+
onError(e);
|
|
35
65
|
});
|
|
36
66
|
return () => {
|
|
37
67
|
myCall.leave().catch((e) => {
|
|
@@ -39,13 +69,94 @@ export const LivestreamPlayer = (props: LivestreamPlayerProps) => {
|
|
|
39
69
|
});
|
|
40
70
|
setCall(undefined);
|
|
41
71
|
};
|
|
42
|
-
}, [callId, callType, client]);
|
|
72
|
+
}, [callId, callType, client, onError]);
|
|
43
73
|
|
|
44
|
-
if (!call)
|
|
74
|
+
if (!call) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
45
77
|
|
|
46
78
|
return (
|
|
47
79
|
<StreamCall call={call}>
|
|
48
|
-
<
|
|
80
|
+
<LivestreamCall {...restProps} />
|
|
49
81
|
</StreamCall>
|
|
50
82
|
);
|
|
51
83
|
};
|
|
84
|
+
|
|
85
|
+
const LivestreamCall = (props: {
|
|
86
|
+
joinBehavior?: 'asap' | 'live';
|
|
87
|
+
layoutProps?: LivestreamLayoutProps;
|
|
88
|
+
backstageProps?: BackstageLayoutProps;
|
|
89
|
+
onError?: (error: any) => void;
|
|
90
|
+
}) => {
|
|
91
|
+
const call = useLivestreamCall(props);
|
|
92
|
+
const { useIsCallLive } = useCallStateHooks();
|
|
93
|
+
const isLive = useIsCallLive();
|
|
94
|
+
|
|
95
|
+
if (!call) return null;
|
|
96
|
+
|
|
97
|
+
if (isLive) {
|
|
98
|
+
return <LivestreamLayout {...props.layoutProps} />;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return <BackstageLayout {...props.backstageProps} />;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const useLivestreamCall = (props: {
|
|
105
|
+
joinBehavior?: 'asap' | 'live';
|
|
106
|
+
onError?: (error: any) => void;
|
|
107
|
+
}) => {
|
|
108
|
+
const call = useCall();
|
|
109
|
+
const { useIsCallLive, useOwnCapabilities } = useCallStateHooks();
|
|
110
|
+
const canJoinLive = useIsCallLive();
|
|
111
|
+
const canJoinEarly = useCanJoinEearly();
|
|
112
|
+
const canJoinBackstage =
|
|
113
|
+
useOwnCapabilities()?.includes('join-backstage') ?? false;
|
|
114
|
+
const canJoinAsap = canJoinLive || canJoinEarly || canJoinBackstage;
|
|
115
|
+
const joinBehavior = props.joinBehavior ?? 'asap';
|
|
116
|
+
const canJoin =
|
|
117
|
+
(joinBehavior === 'asap' && canJoinAsap) ||
|
|
118
|
+
(joinBehavior === 'live' && canJoinLive);
|
|
119
|
+
const onError = useEffectEvent(props.onError);
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (call && call.state.callingState === CallingState.IDLE && canJoin) {
|
|
123
|
+
call.join().catch((e) => {
|
|
124
|
+
console.error('Failed to join call', e);
|
|
125
|
+
onError(e);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}, [call, canJoin, onError]);
|
|
129
|
+
|
|
130
|
+
return call;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const useCanJoinEearly = () => {
|
|
134
|
+
const { useCallStartsAt, useCallSettings } = useCallStateHooks();
|
|
135
|
+
const startsAt = useCallStartsAt();
|
|
136
|
+
const settings = useCallSettings();
|
|
137
|
+
const joinAheadTimeSeconds = settings?.backstage.join_ahead_time_seconds;
|
|
138
|
+
const [canJoinEarly, setCanJoinEearly] = useState(() =>
|
|
139
|
+
checkCanJoinEarly(startsAt, joinAheadTimeSeconds),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!canJoinEarly) {
|
|
144
|
+
const handle = setInterval(() => {
|
|
145
|
+
setCanJoinEearly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
|
|
146
|
+
}, 1000);
|
|
147
|
+
|
|
148
|
+
return () => clearInterval(handle);
|
|
149
|
+
}
|
|
150
|
+
}, [canJoinEarly, startsAt, joinAheadTimeSeconds]);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const checkCanJoinEarly = (
|
|
154
|
+
startsAt: Date | undefined,
|
|
155
|
+
joinAheadTimeSeconds: number | undefined,
|
|
156
|
+
) => {
|
|
157
|
+
if (!startsAt) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return Date.now() >= +startsAt - (joinAheadTimeSeconds ?? 0) * 1000;
|
|
162
|
+
};
|