@stream-io/video-react-native-sdk 0.6.2 → 0.6.3

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 (196) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/commonjs/components/Call/CallContent/CallContent.js +3 -0
  3. package/dist/commonjs/components/Call/CallContent/CallContent.js.map +1 -1
  4. package/dist/commonjs/components/Call/CallControls/{ScreenShareButton.js → ScreenShareToggleButton.js} +44 -43
  5. package/dist/commonjs/components/Call/CallControls/ScreenShareToggleButton.js.map +1 -0
  6. package/dist/commonjs/components/Call/CallControls/index.js +4 -4
  7. package/dist/commonjs/components/Call/CallControls/index.js.map +1 -1
  8. package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js +3 -2
  9. package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js.map +1 -1
  10. package/dist/commonjs/components/Livestream/HostLivestream/HostLivestream.js +59 -6
  11. package/dist/commonjs/components/Livestream/HostLivestream/HostLivestream.js.map +1 -1
  12. package/dist/commonjs/components/Livestream/LivestreamControls/HostLivestreamControls.js +7 -3
  13. package/dist/commonjs/components/Livestream/LivestreamControls/HostLivestreamControls.js.map +1 -1
  14. package/dist/commonjs/components/Livestream/LivestreamControls/HostStartStreamButton.js +13 -9
  15. package/dist/commonjs/components/Livestream/LivestreamControls/HostStartStreamButton.js.map +1 -1
  16. package/dist/commonjs/components/Livestream/LivestreamControls/LivestreamScreenShareToggleButton.js +126 -0
  17. package/dist/commonjs/components/Livestream/LivestreamControls/LivestreamScreenShareToggleButton.js.map +1 -0
  18. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLivestreamControls.js +4 -2
  19. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLivestreamControls.js.map +1 -1
  20. package/dist/commonjs/components/Livestream/LivestreamControls/index.js +44 -11
  21. package/dist/commonjs/components/Livestream/LivestreamControls/index.js.map +1 -1
  22. package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js +45 -6
  23. package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js.map +1 -1
  24. package/dist/commonjs/components/Livestream/LivestreamTopView/HostLivestreamTopView.js +4 -2
  25. package/dist/commonjs/components/Livestream/LivestreamTopView/HostLivestreamTopView.js.map +1 -1
  26. package/dist/commonjs/components/Livestream/LivestreamTopView/ViewerLivestreamTopView.js +4 -2
  27. package/dist/commonjs/components/Livestream/LivestreamTopView/ViewerLivestreamTopView.js.map +1 -1
  28. package/dist/commonjs/components/Livestream/LivestreamTopView/index.js +11 -0
  29. package/dist/commonjs/components/Livestream/LivestreamTopView/index.js.map +1 -1
  30. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js +24 -23
  31. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js.map +1 -1
  32. package/dist/commonjs/components/Participant/FloatingParticipantView/index.js +4 -3
  33. package/dist/commonjs/components/Participant/FloatingParticipantView/index.js.map +1 -1
  34. package/dist/commonjs/components/Participant/ParticipantView/VideoRenderer.js +33 -9
  35. package/dist/commonjs/components/Participant/ParticipantView/VideoRenderer.js.map +1 -1
  36. package/dist/commonjs/components/utility/ScreenShareOverlay.js +92 -0
  37. package/dist/commonjs/components/utility/ScreenShareOverlay.js.map +1 -0
  38. package/dist/commonjs/components/utility/index.js +11 -0
  39. package/dist/commonjs/components/utility/index.js.map +1 -1
  40. package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js +5 -3
  41. package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
  42. package/dist/commonjs/icons/StopScreenShare.js +26 -0
  43. package/dist/commonjs/icons/StopScreenShare.js.map +1 -0
  44. package/dist/commonjs/icons/index.js +11 -0
  45. package/dist/commonjs/icons/index.js.map +1 -1
  46. package/dist/commonjs/theme/theme.js +13 -4
  47. package/dist/commonjs/theme/theme.js.map +1 -1
  48. package/dist/commonjs/translations/en.json +2 -0
  49. package/dist/commonjs/utils/StreamVideoRN/index.js +2 -1
  50. package/dist/commonjs/utils/StreamVideoRN/index.js.map +1 -1
  51. package/dist/commonjs/utils/internal/pushLogoutCallback.js.map +1 -1
  52. package/dist/commonjs/utils/push/android.js +6 -4
  53. package/dist/commonjs/utils/push/android.js.map +1 -1
  54. package/dist/commonjs/utils/push/ios.js +6 -4
  55. package/dist/commonjs/utils/push/ios.js.map +1 -1
  56. package/dist/commonjs/version.js +1 -1
  57. package/dist/module/components/Call/CallContent/CallContent.js +3 -0
  58. package/dist/module/components/Call/CallContent/CallContent.js.map +1 -1
  59. package/dist/module/components/Call/CallControls/{ScreenShareButton.js → ScreenShareToggleButton.js} +41 -40
  60. package/dist/module/components/Call/CallControls/ScreenShareToggleButton.js.map +1 -0
  61. package/dist/module/components/Call/CallControls/index.js +1 -1
  62. package/dist/module/components/Call/CallControls/index.js.map +1 -1
  63. package/dist/module/components/Call/CallLayout/CallParticipantsSpotlight.js +3 -2
  64. package/dist/module/components/Call/CallLayout/CallParticipantsSpotlight.js.map +1 -1
  65. package/dist/module/components/Livestream/HostLivestream/HostLivestream.js +60 -7
  66. package/dist/module/components/Livestream/HostLivestream/HostLivestream.js.map +1 -1
  67. package/dist/module/components/Livestream/LivestreamControls/HostLivestreamControls.js +7 -3
  68. package/dist/module/components/Livestream/LivestreamControls/HostLivestreamControls.js.map +1 -1
  69. package/dist/module/components/Livestream/LivestreamControls/HostStartStreamButton.js +13 -9
  70. package/dist/module/components/Livestream/LivestreamControls/HostStartStreamButton.js.map +1 -1
  71. package/dist/module/components/Livestream/LivestreamControls/LivestreamScreenShareToggleButton.js +117 -0
  72. package/dist/module/components/Livestream/LivestreamControls/LivestreamScreenShareToggleButton.js.map +1 -0
  73. package/dist/module/components/Livestream/LivestreamControls/ViewerLivestreamControls.js +4 -2
  74. package/dist/module/components/Livestream/LivestreamControls/ViewerLivestreamControls.js.map +1 -1
  75. package/dist/module/components/Livestream/LivestreamControls/index.js +4 -1
  76. package/dist/module/components/Livestream/LivestreamControls/index.js.map +1 -1
  77. package/dist/module/components/Livestream/LivestreamLayout/LivestreamLayout.js +43 -5
  78. package/dist/module/components/Livestream/LivestreamLayout/LivestreamLayout.js.map +1 -1
  79. package/dist/module/components/Livestream/LivestreamTopView/HostLivestreamTopView.js +4 -2
  80. package/dist/module/components/Livestream/LivestreamTopView/HostLivestreamTopView.js.map +1 -1
  81. package/dist/module/components/Livestream/LivestreamTopView/ViewerLivestreamTopView.js +4 -2
  82. package/dist/module/components/Livestream/LivestreamTopView/ViewerLivestreamTopView.js.map +1 -1
  83. package/dist/module/components/Livestream/LivestreamTopView/index.js +1 -0
  84. package/dist/module/components/Livestream/LivestreamTopView/index.js.map +1 -1
  85. package/dist/module/components/Livestream/ViewerLivestream/ViewerLivestream.js +24 -24
  86. package/dist/module/components/Livestream/ViewerLivestream/ViewerLivestream.js.map +1 -1
  87. package/dist/module/components/Participant/FloatingParticipantView/index.js +4 -3
  88. package/dist/module/components/Participant/FloatingParticipantView/index.js.map +1 -1
  89. package/dist/module/components/Participant/ParticipantView/VideoRenderer.js +33 -9
  90. package/dist/module/components/Participant/ParticipantView/VideoRenderer.js.map +1 -1
  91. package/dist/module/components/utility/ScreenShareOverlay.js +85 -0
  92. package/dist/module/components/utility/ScreenShareOverlay.js.map +1 -0
  93. package/dist/module/components/utility/index.js +1 -0
  94. package/dist/module/components/utility/index.js.map +1 -1
  95. package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js +5 -3
  96. package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
  97. package/dist/module/icons/StopScreenShare.js +16 -0
  98. package/dist/module/icons/StopScreenShare.js.map +1 -0
  99. package/dist/module/icons/index.js +1 -0
  100. package/dist/module/icons/index.js.map +1 -1
  101. package/dist/module/theme/theme.js +13 -4
  102. package/dist/module/theme/theme.js.map +1 -1
  103. package/dist/module/translations/en.json +2 -0
  104. package/dist/module/utils/StreamVideoRN/index.js +2 -1
  105. package/dist/module/utils/StreamVideoRN/index.js.map +1 -1
  106. package/dist/module/utils/internal/pushLogoutCallback.js.map +1 -1
  107. package/dist/module/utils/push/android.js +6 -4
  108. package/dist/module/utils/push/android.js.map +1 -1
  109. package/dist/module/utils/push/ios.js +6 -4
  110. package/dist/module/utils/push/ios.js.map +1 -1
  111. package/dist/module/version.js +1 -1
  112. package/dist/typescript/components/Call/CallContent/CallContent.d.ts +6 -1
  113. package/dist/typescript/components/Call/CallContent/CallContent.d.ts.map +1 -1
  114. package/dist/typescript/components/Call/CallControls/{ScreenShareButton.d.ts → ScreenShareToggleButton.d.ts} +3 -3
  115. package/dist/typescript/components/Call/CallControls/ScreenShareToggleButton.d.ts.map +1 -0
  116. package/dist/typescript/components/Call/CallControls/index.d.ts +1 -1
  117. package/dist/typescript/components/Call/CallControls/index.d.ts.map +1 -1
  118. package/dist/typescript/components/Call/CallLayout/CallParticipantsSpotlight.d.ts +2 -2
  119. package/dist/typescript/components/Call/CallLayout/CallParticipantsSpotlight.d.ts.map +1 -1
  120. package/dist/typescript/components/Livestream/HostLivestream/HostLivestream.d.ts +16 -1
  121. package/dist/typescript/components/Livestream/HostLivestream/HostLivestream.d.ts.map +1 -1
  122. package/dist/typescript/components/Livestream/LivestreamControls/HostLivestreamControls.d.ts +7 -1
  123. package/dist/typescript/components/Livestream/LivestreamControls/HostLivestreamControls.d.ts.map +1 -1
  124. package/dist/typescript/components/Livestream/LivestreamControls/HostStartStreamButton.d.ts +7 -3
  125. package/dist/typescript/components/Livestream/LivestreamControls/HostStartStreamButton.d.ts.map +1 -1
  126. package/dist/typescript/components/Livestream/LivestreamControls/LivestreamScreenShareToggleButton.d.ts +7 -0
  127. package/dist/typescript/components/Livestream/LivestreamControls/LivestreamScreenShareToggleButton.d.ts.map +1 -0
  128. package/dist/typescript/components/Livestream/LivestreamControls/ViewerLivestreamControls.d.ts +3 -1
  129. package/dist/typescript/components/Livestream/LivestreamControls/ViewerLivestreamControls.d.ts.map +1 -1
  130. package/dist/typescript/components/Livestream/LivestreamControls/index.d.ts +4 -1
  131. package/dist/typescript/components/Livestream/LivestreamControls/index.d.ts.map +1 -1
  132. package/dist/typescript/components/Livestream/LivestreamLayout/LivestreamLayout.d.ts +6 -1
  133. package/dist/typescript/components/Livestream/LivestreamLayout/LivestreamLayout.d.ts.map +1 -1
  134. package/dist/typescript/components/Livestream/LivestreamTopView/HostLivestreamTopView.d.ts +3 -1
  135. package/dist/typescript/components/Livestream/LivestreamTopView/HostLivestreamTopView.d.ts.map +1 -1
  136. package/dist/typescript/components/Livestream/LivestreamTopView/ViewerLivestreamTopView.d.ts +3 -1
  137. package/dist/typescript/components/Livestream/LivestreamTopView/ViewerLivestreamTopView.d.ts.map +1 -1
  138. package/dist/typescript/components/Livestream/LivestreamTopView/index.d.ts +1 -0
  139. package/dist/typescript/components/Livestream/LivestreamTopView/index.d.ts.map +1 -1
  140. package/dist/typescript/components/Livestream/ViewerLivestream/ViewerLivestream.d.ts.map +1 -1
  141. package/dist/typescript/components/Participant/FloatingParticipantView/index.d.ts +8 -3
  142. package/dist/typescript/components/Participant/FloatingParticipantView/index.d.ts.map +1 -1
  143. package/dist/typescript/components/Participant/ParticipantView/VideoRenderer.d.ts.map +1 -1
  144. package/dist/typescript/components/utility/ScreenShareOverlay.d.ts +10 -0
  145. package/dist/typescript/components/utility/ScreenShareOverlay.d.ts.map +1 -0
  146. package/dist/typescript/components/utility/index.d.ts +1 -0
  147. package/dist/typescript/components/utility/index.d.ts.map +1 -1
  148. package/dist/typescript/hooks/push/useIosVoipPushEventsSetupEffect.d.ts.map +1 -1
  149. package/dist/typescript/icons/StopScreenShare.d.ts +8 -0
  150. package/dist/typescript/icons/StopScreenShare.d.ts.map +1 -0
  151. package/dist/typescript/icons/index.d.ts +1 -0
  152. package/dist/typescript/icons/index.d.ts.map +1 -1
  153. package/dist/typescript/theme/theme.d.ts +12 -3
  154. package/dist/typescript/theme/theme.d.ts.map +1 -1
  155. package/dist/typescript/translations/index.d.ts +2 -0
  156. package/dist/typescript/translations/index.d.ts.map +1 -1
  157. package/dist/typescript/utils/StreamVideoRN/index.d.ts +1 -1
  158. package/dist/typescript/utils/StreamVideoRN/index.d.ts.map +1 -1
  159. package/dist/typescript/utils/internal/pushLogoutCallback.d.ts +2 -2
  160. package/dist/typescript/utils/internal/pushLogoutCallback.d.ts.map +1 -1
  161. package/dist/typescript/utils/push/android.d.ts.map +1 -1
  162. package/dist/typescript/utils/push/ios.d.ts.map +1 -1
  163. package/dist/typescript/version.d.ts +1 -1
  164. package/package.json +1 -1
  165. package/src/components/Call/CallContent/CallContent.tsx +10 -0
  166. package/src/components/Call/CallControls/{ScreenShareButton.tsx → ScreenShareToggleButton.tsx} +59 -41
  167. package/src/components/Call/CallControls/index.tsx +1 -1
  168. package/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx +32 -24
  169. package/src/components/Livestream/HostLivestream/HostLivestream.tsx +92 -6
  170. package/src/components/Livestream/LivestreamControls/HostLivestreamControls.tsx +10 -1
  171. package/src/components/Livestream/LivestreamControls/HostStartStreamButton.tsx +18 -10
  172. package/src/components/Livestream/LivestreamControls/LivestreamScreenShareToggleButton.tsx +161 -0
  173. package/src/components/Livestream/LivestreamControls/ViewerLivestreamControls.tsx +4 -1
  174. package/src/components/Livestream/LivestreamControls/index.ts +4 -1
  175. package/src/components/Livestream/LivestreamLayout/LivestreamLayout.tsx +66 -6
  176. package/src/components/Livestream/LivestreamTopView/HostLivestreamTopView.tsx +4 -1
  177. package/src/components/Livestream/LivestreamTopView/ViewerLivestreamTopView.tsx +4 -1
  178. package/src/components/Livestream/LivestreamTopView/index.ts +1 -0
  179. package/src/components/Livestream/ViewerLivestream/ViewerLivestream.tsx +39 -38
  180. package/src/components/Participant/FloatingParticipantView/index.tsx +15 -5
  181. package/src/components/Participant/ParticipantView/VideoRenderer.tsx +54 -8
  182. package/src/components/utility/ScreenShareOverlay.tsx +106 -0
  183. package/src/components/utility/index.ts +1 -0
  184. package/src/hooks/push/useIosVoipPushEventsSetupEffect.ts +5 -3
  185. package/src/icons/StopScreenShare.tsx +22 -0
  186. package/src/icons/index.tsx +1 -0
  187. package/src/theme/theme.ts +24 -6
  188. package/src/translations/en.json +2 -0
  189. package/src/utils/StreamVideoRN/index.ts +4 -1
  190. package/src/utils/internal/pushLogoutCallback.ts +2 -2
  191. package/src/utils/push/android.ts +6 -4
  192. package/src/utils/push/ios.ts +6 -4
  193. package/src/version.ts +1 -1
  194. package/dist/commonjs/components/Call/CallControls/ScreenShareButton.js.map +0 -1
  195. package/dist/module/components/Call/CallControls/ScreenShareButton.js.map +0 -1
  196. package/dist/typescript/components/Call/CallControls/ScreenShareButton.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect } from 'react';
2
- import { StyleSheet, SafeAreaView } from 'react-native';
2
+ import { StyleSheet, View } from 'react-native';
3
3
  import InCallManager from 'react-native-incall-manager';
4
4
 
5
5
  import { useTheme } from '../../../contexts';
@@ -15,6 +15,17 @@ import {
15
15
  LivestreamLayout as DefaultLivestreamLayout,
16
16
  LivestreamLayoutProps,
17
17
  } from '../LivestreamLayout';
18
+ import { Z_INDEX } from '../../../constants';
19
+ import {
20
+ FloatingParticipantView as DefaultFloatingParticipantView,
21
+ FloatingParticipantViewProps,
22
+ } from '../../Participant/FloatingParticipantView';
23
+ import { useCallStateHooks } from '@stream-io/video-react-bindings';
24
+ import { SfuModels, StreamVideoParticipant } from '@stream-io/video-client';
25
+ import {
26
+ ScreenShareOverlay as DefaultScreenShaerOverlay,
27
+ ScreenShareOverlayProps,
28
+ } from '../../utility/ScreenShareOverlay';
18
29
 
19
30
  /**
20
31
  * Props for the HostLivestream component.
@@ -33,12 +44,28 @@ export type HostLivestreamProps = HostLivestreamTopViewProps &
33
44
  * Component to customize the bottom view controls at the host's live stream.
34
45
  */
35
46
  HostLivestreamControls?: React.ComponentType<HostLivestreamControlsProps> | null;
47
+ /**
48
+ * Component to customize the FloatingParticipantView when screen is shared.
49
+ */
50
+ FloatingParticipantView?: React.ComponentType<FloatingParticipantViewProps> | null;
51
+ /**
52
+ * Component to customize the ScreenShareOverlay.
53
+ */
54
+ ScreenShareOverlay?: React.ComponentType<ScreenShareOverlayProps> | null;
36
55
  /**
37
56
  * Enable HTTP live streaming
38
57
  */
39
58
  hls?: boolean;
59
+ /**
60
+ * Should the published streams be stopped if the host end the livestream.
61
+ * @default true
62
+ */
63
+ stopPublishedStreamsOnEndStream?: boolean;
40
64
  };
41
65
 
66
+ const hasVideoTrack = (p?: StreamVideoParticipant) =>
67
+ p?.publishedTracks.includes(SfuModels.TrackType.VIDEO);
68
+
42
69
  /**
43
70
  * The HostLivestream component displays the UI for the Host's live stream.
44
71
  */
@@ -46,6 +73,8 @@ export const HostLivestream = ({
46
73
  HostLivestreamTopView = DefaultHostLivestreamTopView,
47
74
  HostLivestreamControls = DefaultHostLivestreamControls,
48
75
  LivestreamLayout = DefaultLivestreamLayout,
76
+ FloatingParticipantView = DefaultFloatingParticipantView,
77
+ ScreenShareOverlay = DefaultScreenShaerOverlay,
49
78
  LiveIndicator,
50
79
  FollowerCount,
51
80
  DurationBadge,
@@ -54,35 +83,74 @@ export const HostLivestream = ({
54
83
  onEndStreamHandler,
55
84
  onStartStreamHandler,
56
85
  hls = false,
86
+ stopPublishedStreamsOnEndStream = true,
57
87
  }: HostLivestreamProps) => {
58
88
  const {
59
89
  theme: { colors, hostLivestream },
60
90
  } = useTheme();
61
91
 
92
+ const { useParticipants, useHasOngoingScreenShare } = useCallStateHooks();
93
+ const [currentSpeaker] = useParticipants();
94
+ const hasOngoingScreenShare = useHasOngoingScreenShare();
95
+ const floatingParticipant =
96
+ hasOngoingScreenShare && hasVideoTrack(currentSpeaker) && currentSpeaker;
97
+
62
98
  // Automatically route audio to speaker devices as relevant for watching videos.
63
99
  useEffect(() => {
64
100
  InCallManager.start({ media: 'video' });
65
101
  return () => InCallManager.stop();
66
102
  }, []);
67
103
 
104
+ const [topViewHeight, setTopViewHeight] = React.useState<number>();
105
+ const [controlsHeight, setControlsHeight] = React.useState<number>();
106
+
68
107
  const topViewProps: HostLivestreamTopViewProps = {
69
108
  LiveIndicator,
70
109
  FollowerCount,
71
110
  DurationBadge,
111
+ onLayout: (event) => {
112
+ setTopViewHeight(event.nativeEvent.layout.height);
113
+ },
72
114
  };
73
115
 
74
116
  return (
75
- <SafeAreaView
117
+ <View
76
118
  style={[
77
119
  styles.container,
78
120
  {
79
- backgroundColor: colors.static_grey,
121
+ backgroundColor: colors.dark_gray,
80
122
  },
81
123
  hostLivestream.container,
82
124
  ]}
83
125
  >
84
- {HostLivestreamTopView && <HostLivestreamTopView {...topViewProps} />}
85
- {LivestreamLayout && <LivestreamLayout />}
126
+ {HostLivestreamTopView && (
127
+ <View
128
+ style={styles.topViewContainer}
129
+ onLayout={(event) => {
130
+ setTopViewHeight(event.nativeEvent.layout.height);
131
+ }}
132
+ >
133
+ <HostLivestreamTopView {...topViewProps} />
134
+ </View>
135
+ )}
136
+ {FloatingParticipantView &&
137
+ floatingParticipant &&
138
+ topViewHeight &&
139
+ controlsHeight && (
140
+ <FloatingParticipantView
141
+ participant={floatingParticipant}
142
+ draggableContainerStyle={[
143
+ StyleSheet.absoluteFill,
144
+ {
145
+ top: topViewHeight,
146
+ bottom: controlsHeight,
147
+ },
148
+ ]}
149
+ />
150
+ )}
151
+ {LivestreamLayout && (
152
+ <LivestreamLayout ScreenShareOverlay={ScreenShareOverlay} />
153
+ )}
86
154
  {HostLivestreamControls && (
87
155
  <HostLivestreamControls
88
156
  onEndStreamHandler={onEndStreamHandler}
@@ -90,9 +158,13 @@ export const HostLivestream = ({
90
158
  HostStartStreamButton={HostStartStreamButton}
91
159
  LivestreamMediaControls={LivestreamMediaControls}
92
160
  hls={hls}
161
+ onLayout={(event) => {
162
+ setControlsHeight(event.nativeEvent.layout.height);
163
+ }}
164
+ stopPublishedStreamsOnEndStream={stopPublishedStreamsOnEndStream}
93
165
  />
94
166
  )}
95
- </SafeAreaView>
167
+ </View>
96
168
  );
97
169
  };
98
170
 
@@ -100,4 +172,18 @@ const styles = StyleSheet.create({
100
172
  container: {
101
173
  flex: 1,
102
174
  },
175
+ topViewContainer: {
176
+ position: 'absolute',
177
+ top: 0,
178
+ left: 0,
179
+ right: 0,
180
+ zIndex: Z_INDEX.IN_FRONT,
181
+ },
182
+ controlsViewContainer: {
183
+ position: 'absolute',
184
+ bottom: 0,
185
+ left: 0,
186
+ right: 0,
187
+ zIndex: Z_INDEX.IN_FRONT,
188
+ },
103
189
  });
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { StyleSheet, View } from 'react-native';
2
+ import { StyleSheet, View, ViewProps } from 'react-native';
3
3
  import {
4
4
  HostStartStreamButton as DefaultHostStartStreamButton,
5
5
  HostStartStreamButtonProps,
@@ -27,6 +27,11 @@ export type HostLivestreamControlsProps = HostStartStreamButtonProps & {
27
27
  * Enable HTTP live streaming
28
28
  */
29
29
  hls?: boolean;
30
+ onLayout?: ViewProps['onLayout'];
31
+ /**
32
+ * Should the published streams be stopped if the host end the livestream.
33
+ */
34
+ stopPublishedStreamsOnEndStream: boolean;
30
35
  };
31
36
 
32
37
  /**
@@ -38,6 +43,8 @@ export const HostLivestreamControls = ({
38
43
  onEndStreamHandler,
39
44
  onStartStreamHandler,
40
45
  hls = false,
46
+ stopPublishedStreamsOnEndStream,
47
+ onLayout,
41
48
  }: HostLivestreamControlsProps) => {
42
49
  const {
43
50
  theme: { colors, hostLivestreamControls },
@@ -49,6 +56,7 @@ export const HostLivestreamControls = ({
49
56
  { backgroundColor: colors.static_overlay },
50
57
  hostLivestreamControls.container,
51
58
  ]}
59
+ onLayout={onLayout}
52
60
  >
53
61
  <View style={[styles.leftElement, hostLivestreamControls.leftElement]}>
54
62
  {HostStartStreamButton && (
@@ -56,6 +64,7 @@ export const HostLivestreamControls = ({
56
64
  onEndStreamHandler={onEndStreamHandler}
57
65
  onStartStreamHandler={onStartStreamHandler}
58
66
  hls={hls}
67
+ stopPublishedStreamsOnEndStream={stopPublishedStreamsOnEndStream}
59
68
  />
60
69
  )}
61
70
  </View>
@@ -13,18 +13,19 @@ import {
13
13
  } from 'react-native';
14
14
  import { useTheme } from '../../../contexts';
15
15
  import { EndBroadcastIcon, StartStreamIcon } from '../../../icons';
16
+ import { SfuModels } from '@stream-io/video-client';
16
17
 
17
18
  /**
18
19
  * Props for the HostStartStreamButton component.
19
20
  */
20
21
  export type HostStartStreamButtonProps = {
21
22
  /**
22
- * Handler to be called when the Start Stream button is pressed.
23
+ * Handler to be called after the Start Stream button is pressed.
23
24
  * @returns void
24
25
  */
25
26
  onStartStreamHandler?: () => void;
26
27
  /**
27
- * Handler to be called when the End Stream button is pressed.
28
+ * Handler to be called after the End Stream button is pressed.
28
29
  * @returns void
29
30
  */
30
31
  onEndStreamHandler?: () => void;
@@ -32,6 +33,10 @@ export type HostStartStreamButtonProps = {
32
33
  * Enable HTTP live streaming
33
34
  */
34
35
  hls?: boolean;
36
+ /**
37
+ * Should the published streams be stopped if the host end the livestream.
38
+ */
39
+ stopPublishedStreamsOnEndStream: boolean;
35
40
  };
36
41
 
37
42
  /**
@@ -41,6 +46,7 @@ export const HostStartStreamButton = ({
41
46
  onEndStreamHandler,
42
47
  onStartStreamHandler,
43
48
  hls,
49
+ stopPublishedStreamsOnEndStream,
44
50
  }: HostStartStreamButtonProps) => {
45
51
  const [isAwaitingResponse, setIsAwaitingResponse] = useState(false);
46
52
  const { useIsCallLive, useIsCallHLSBroadcastingInProgress } =
@@ -62,10 +68,6 @@ export const HostStartStreamButton = ({
62
68
  const liveOrBroadcasting = isCallLive || isCallBroadcasting;
63
69
 
64
70
  const onStartStreamButtonPress = async () => {
65
- if (onStartStreamHandler) {
66
- onStartStreamHandler();
67
- return;
68
- }
69
71
  try {
70
72
  setIsAwaitingResponse(true);
71
73
  await call?.goLive();
@@ -73,18 +75,21 @@ export const HostStartStreamButton = ({
73
75
  await call?.startHLS();
74
76
  }
75
77
  setIsAwaitingResponse(false);
78
+ if (onStartStreamHandler) {
79
+ onStartStreamHandler();
80
+ }
76
81
  } catch (error) {
77
82
  console.error('Error starting livestream', error);
78
83
  }
79
84
  };
80
85
 
81
86
  const onEndStreamButtonPress = async () => {
82
- if (onEndStreamHandler) {
83
- onEndStreamHandler();
84
- return;
85
- }
86
87
  try {
87
88
  setIsAwaitingResponse(true);
89
+ if (stopPublishedStreamsOnEndStream) {
90
+ await call?.stopPublish(SfuModels.TrackType.VIDEO);
91
+ await call?.stopPublish(SfuModels.TrackType.SCREEN_SHARE);
92
+ }
88
93
  if (hls) {
89
94
  await call?.stopHLS();
90
95
  } else {
@@ -92,6 +97,9 @@ export const HostStartStreamButton = ({
92
97
  }
93
98
 
94
99
  setIsAwaitingResponse(false);
100
+ if (onEndStreamHandler) {
101
+ onEndStreamHandler();
102
+ }
95
103
  } catch (error) {
96
104
  console.error('Error stopping livestream', error);
97
105
  }
@@ -0,0 +1,161 @@
1
+ import React, { useEffect } from 'react';
2
+ import { useTheme } from '../../../contexts/ThemeContext';
3
+ import {
4
+ NativeModules,
5
+ Platform,
6
+ Pressable,
7
+ StyleSheet,
8
+ View,
9
+ findNodeHandle,
10
+ } from 'react-native';
11
+ import { ScreenShare } from '../../../icons/ScreenShare';
12
+ import { StopScreenShare } from '../../../icons/StopScreenShare';
13
+ import { ScreenCapturePickerView } from '@stream-io/react-native-webrtc';
14
+ import { SfuModels } from '@stream-io/video-client';
15
+ import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
16
+ import { useIsIosScreenshareBroadcastStarted } from '../../../hooks/useIsIosScreenshareBroadcastStarted';
17
+ import { usePrevious } from '../../../utils/hooks/usePrevious';
18
+
19
+ export type LivestreamScreenShareToggleButtonProps = {};
20
+
21
+ // ios >= 14.0 or android - platform restrictions
22
+ const CanDeviceScreenShare =
23
+ (Platform.OS === 'ios' &&
24
+ // @ts-ignore
25
+ Number.parseInt(Platform.Version.split('.')[0], 10) >= 14) ||
26
+ Platform.OS === 'android';
27
+
28
+ /**
29
+ * The LivestreamVideoControlButton controls the screenshare stream publish/unpublish while in the livestream for the host.
30
+ */
31
+ export const LivestreamScreenShareToggleButton = () => {
32
+ const {
33
+ theme: {
34
+ colors,
35
+ variants: { iconSizes, buttonSizes },
36
+ livestreamScreenShareToggleButton,
37
+ },
38
+ } = useTheme();
39
+
40
+ const call = useCall();
41
+ const { useLocalParticipant, useCallSettings } = useCallStateHooks();
42
+ const callSettings = useCallSettings();
43
+ const isScreenSharingEnabledInCall = callSettings?.screensharing.enabled;
44
+
45
+ const iosScreenShareStartedFromSystem = useIsIosScreenshareBroadcastStarted();
46
+ const prevIosScreenShareStartedFromSystem = usePrevious(
47
+ iosScreenShareStartedFromSystem,
48
+ );
49
+
50
+ const localParticipant = useLocalParticipant();
51
+ const hasPublishedScreenShare = localParticipant?.publishedTracks.includes(
52
+ SfuModels.TrackType.SCREEN_SHARE,
53
+ );
54
+
55
+ // listens to iOS screen share broadcast started event from the system
56
+ useEffect(() => {
57
+ const run = async () => {
58
+ if (Platform.OS !== 'ios') {
59
+ return;
60
+ }
61
+ if (
62
+ iosScreenShareStartedFromSystem &&
63
+ !prevIosScreenShareStartedFromSystem
64
+ ) {
65
+ const media = await navigator.mediaDevices.getDisplayMedia({
66
+ // @ts-ignore
67
+ deviceId: 'broadcast',
68
+ video: true,
69
+ audio: true,
70
+ });
71
+ await call?.publishScreenShareStream(media);
72
+ } else if (
73
+ !iosScreenShareStartedFromSystem &&
74
+ prevIosScreenShareStartedFromSystem
75
+ ) {
76
+ await call?.stopPublish(SfuModels.TrackType.SCREEN_SHARE);
77
+ }
78
+ };
79
+ run();
80
+ }, [
81
+ call,
82
+ iosScreenShareStartedFromSystem,
83
+ prevIosScreenShareStartedFromSystem,
84
+ ]);
85
+
86
+ const screenCapturePickerViewiOSRef = React.useRef(null);
87
+
88
+ const onPress = async () => {
89
+ if (!hasPublishedScreenShare) {
90
+ if (Platform.OS === 'ios') {
91
+ const reactTag = findNodeHandle(screenCapturePickerViewiOSRef.current);
92
+ await NativeModules.ScreenCapturePickerViewManager.show(reactTag);
93
+ // After this the iOS screen share broadcast started/stopped event will be triggered
94
+ // and the useEffect listener will handle the rest
95
+ } else {
96
+ try {
97
+ const media = await navigator.mediaDevices.getDisplayMedia({
98
+ video: true,
99
+ audio: true,
100
+ });
101
+ await call?.publishScreenShareStream(media);
102
+ } catch (e) {
103
+ // ignored.. user didnt allow the screen share in the popup
104
+ }
105
+ }
106
+ } else if (hasPublishedScreenShare) {
107
+ await call?.stopPublish(SfuModels.TrackType.SCREEN_SHARE);
108
+ }
109
+ };
110
+
111
+ if (!isScreenSharingEnabledInCall || !CanDeviceScreenShare) {
112
+ return null;
113
+ }
114
+
115
+ return (
116
+ <Pressable
117
+ onPress={onPress}
118
+ style={[
119
+ styles.container,
120
+ {
121
+ backgroundColor: hasPublishedScreenShare
122
+ ? colors.error
123
+ : colors.dark_gray,
124
+ height: buttonSizes.xs,
125
+ width: buttonSizes.xs,
126
+ },
127
+ livestreamScreenShareToggleButton.container,
128
+ ]}
129
+ >
130
+ <View
131
+ style={[
132
+ styles.icon,
133
+ {
134
+ height: iconSizes.sm,
135
+ width: iconSizes.sm,
136
+ },
137
+ livestreamScreenShareToggleButton.icon,
138
+ ]}
139
+ >
140
+ {hasPublishedScreenShare ? (
141
+ <StopScreenShare color={colors.static_white} />
142
+ ) : (
143
+ <ScreenShare color={colors.static_white} />
144
+ )}
145
+ </View>
146
+ {Platform.OS === 'ios' && (
147
+ <ScreenCapturePickerView ref={screenCapturePickerViewiOSRef} />
148
+ )}
149
+ </Pressable>
150
+ );
151
+ };
152
+
153
+ const styles = StyleSheet.create({
154
+ container: {
155
+ justifyContent: 'center',
156
+ alignItems: 'center',
157
+ marginHorizontal: 4,
158
+ borderRadius: 4,
159
+ },
160
+ icon: {},
161
+ });
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { StyleSheet, View } from 'react-native';
2
+ import { StyleSheet, View, ViewProps } from 'react-native';
3
3
 
4
4
  import {
5
5
  ViewerLeaveStreamButton as DefaultViewerLeaveStreamButton,
@@ -16,6 +16,7 @@ export type ViewerLivestreamControlsProps = ViewerLeaveStreamButtonProps & {
16
16
  * Component to customize the leave stream button on the viewer's end live stream.
17
17
  */
18
18
  ViewerLeaveStreamButton?: React.ComponentType<ViewerLeaveStreamButtonProps> | null;
19
+ onLayout?: ViewProps['onLayout'];
19
20
  };
20
21
 
21
22
  /**
@@ -24,6 +25,7 @@ export type ViewerLivestreamControlsProps = ViewerLeaveStreamButtonProps & {
24
25
  export const ViewerLivestreamControls = ({
25
26
  ViewerLeaveStreamButton = DefaultViewerLeaveStreamButton,
26
27
  onLeaveStreamHandler,
28
+ onLayout,
27
29
  }: ViewerLivestreamControlsProps) => {
28
30
  const {
29
31
  theme: { colors, viewerLivestreamControls },
@@ -38,6 +40,7 @@ export const ViewerLivestreamControls = ({
38
40
  },
39
41
  viewerLivestreamControls.container,
40
42
  ]}
43
+ onLayout={onLayout}
41
44
  >
42
45
  <View style={[styles.leftElement, viewerLivestreamControls.leftElement]}>
43
46
  {ViewerLeaveStreamButton && (
@@ -1,5 +1,8 @@
1
1
  export * from './HostLivestreamControls';
2
- export * from './LivestreamMediaControls';
3
2
  export * from './HostStartStreamButton';
4
3
  export * from './LivestreamAudioControlButton';
4
+ export * from './LivestreamMediaControls';
5
5
  export * from './LivestreamVideoControlButton';
6
+ export * from './ViewerLeaveStreamButton';
7
+ export * from './ViewerLivestreamControls';
8
+ export * from './LivestreamScreenShareToggleButton';
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
2
  import { SfuModels, StreamVideoParticipant } from '@stream-io/video-client';
3
3
  import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
4
4
  import { StyleSheet, View, ViewStyle } from 'react-native';
@@ -8,6 +8,8 @@ import {
8
8
  VideoRenderer as DefaultVideoRenderer,
9
9
  VideoRendererProps,
10
10
  } from '../../Participant';
11
+ import { ScreenShareOverlayProps } from '../../utility/ScreenShareOverlay';
12
+ import { VideoDimension } from '@stream-io/video-client/dist/src/gen/video/sfu/models/models';
11
13
 
12
14
  /**
13
15
  * Props for the LivestreamLayout component.
@@ -22,6 +24,10 @@ export type LivestreamLayoutProps = {
22
24
  * Component to customize the video component of the participant.
23
25
  */
24
26
  VideoRenderer?: React.ComponentType<VideoRendererProps> | null;
27
+ /**
28
+ * Component to customize the ScreenShareOverlay.
29
+ */
30
+ ScreenShareOverlay?: React.ComponentType<ScreenShareOverlayProps> | null;
25
31
  };
26
32
 
27
33
  const hasScreenShare = (p?: StreamVideoParticipant) =>
@@ -33,6 +39,7 @@ const hasScreenShare = (p?: StreamVideoParticipant) =>
33
39
  export const LivestreamLayout = ({
34
40
  landscape,
35
41
  VideoRenderer = DefaultVideoRenderer,
42
+ ScreenShareOverlay,
36
43
  }: LivestreamLayoutProps) => {
37
44
  const { useParticipants, useHasOngoingScreenShare } = useCallStateHooks();
38
45
  const call = useCall();
@@ -47,6 +54,23 @@ export const LivestreamLayout = ({
47
54
 
48
55
  usePaginatedLayoutSortPreset(call);
49
56
 
57
+ const [objectFit, setObjectFit] =
58
+ useState<
59
+ React.ComponentProps<NonNullable<typeof VideoRenderer>>['objectFit']
60
+ >();
61
+
62
+ // no need to pass object fit for local participant as the dimensions are for remote tracks
63
+ const objectFitToBeSet = currentSpeaker?.isLocalParticipant
64
+ ? undefined
65
+ : objectFit;
66
+
67
+ const onDimensionsChange = useCallback((d: VideoDimension | undefined) => {
68
+ if (d) {
69
+ const isWidthWide = d.width > d.height;
70
+ setObjectFit(isWidthWide ? 'contain' : 'cover');
71
+ }
72
+ }, []);
73
+
50
74
  const landScapeStyles: ViewStyle = {
51
75
  flexDirection: landscape ? 'row' : 'column',
52
76
  };
@@ -56,20 +80,56 @@ export const LivestreamLayout = ({
56
80
  style={[
57
81
  styles.container,
58
82
  landScapeStyles,
59
- { backgroundColor: colors.dark_gray },
83
+ { backgroundColor: colors.static_grey },
60
84
  livestreamLayout.container,
61
85
  ]}
62
86
  >
63
- {VideoRenderer && hasOngoingScreenShare && presenter && (
64
- <VideoRenderer trackType="screenShareTrack" participant={presenter} />
65
- )}
87
+ <RemoteVideoTrackDimensionsRenderLessComponent
88
+ onDimensionsChange={onDimensionsChange}
89
+ />
90
+ {VideoRenderer &&
91
+ hasOngoingScreenShare &&
92
+ presenter &&
93
+ (ScreenShareOverlay ? (
94
+ <ScreenShareOverlay />
95
+ ) : (
96
+ <VideoRenderer trackType="screenShareTrack" participant={presenter} />
97
+ ))}
66
98
  {VideoRenderer && !hasOngoingScreenShare && currentSpeaker && (
67
- <VideoRenderer participant={currentSpeaker} trackType="videoTrack" />
99
+ <VideoRenderer
100
+ participant={currentSpeaker}
101
+ objectFit={objectFitToBeSet}
102
+ trackType="videoTrack"
103
+ />
68
104
  )}
69
105
  </View>
70
106
  );
71
107
  };
72
108
 
109
+ const RemoteVideoTrackDimensionsRenderLessComponent = ({
110
+ onDimensionsChange,
111
+ }: {
112
+ onDimensionsChange: (d: VideoDimension | undefined) => void;
113
+ }) => {
114
+ const [dimension, setDimension] = useState<VideoDimension>();
115
+ const { useCallStatsReport } = useCallStateHooks();
116
+ const statsReport = useCallStatsReport();
117
+ const highestFrameHeight = statsReport?.subscriberStats?.highestFrameHeight;
118
+ const highestFrameWidth = statsReport?.subscriberStats?.highestFrameWidth;
119
+
120
+ useEffect(() => {
121
+ if (highestFrameHeight && highestFrameWidth) {
122
+ setDimension({ height: highestFrameHeight, width: highestFrameWidth });
123
+ }
124
+ }, [highestFrameHeight, highestFrameWidth]);
125
+
126
+ useEffect(() => {
127
+ onDimensionsChange(dimension);
128
+ }, [dimension, onDimensionsChange]);
129
+
130
+ return null;
131
+ };
132
+
73
133
  const styles = StyleSheet.create({
74
134
  container: {
75
135
  flex: 1,
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { StyleSheet, View } from 'react-native';
2
+ import { StyleSheet, View, ViewProps } from 'react-native';
3
3
  import {
4
4
  DurationBadge as DefaultDurationBadge,
5
5
  DurationBadgeProps,
@@ -32,6 +32,7 @@ export type HostLivestreamTopViewProps = {
32
32
  * Component to customize the Follower count indicator on the host's live stream's top view.
33
33
  */
34
34
  FollowerCount?: React.ComponentType<FollowerCountProps> | null;
35
+ onLayout?: ViewProps['onLayout'];
35
36
  };
36
37
 
37
38
  /**
@@ -41,6 +42,7 @@ export const HostLivestreamTopView = ({
41
42
  DurationBadge = DefaultDurationBadge,
42
43
  LiveIndicator = DefaultLiveIndicator,
43
44
  FollowerCount = DefaultFollowerCount,
45
+ onLayout,
44
46
  }: HostLivestreamTopViewProps) => {
45
47
  const { useIsCallLive, useIsCallHLSBroadcastingInProgress } =
46
48
  useCallStateHooks();
@@ -58,6 +60,7 @@ export const HostLivestreamTopView = ({
58
60
  { backgroundColor: colors.static_overlay },
59
61
  hostLivestreamTopView.container,
60
62
  ]}
63
+ onLayout={onLayout}
61
64
  >
62
65
  <View style={[styles.leftElement, hostLivestreamTopView.leftElement]}>
63
66
  {DurationBadge && <DurationBadge mode="host" />}
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { StyleSheet, View } from 'react-native';
2
+ import { StyleSheet, View, ViewProps } from 'react-native';
3
3
  import {
4
4
  DurationBadge as DefaultDurationBadge,
5
5
  DurationBadgeProps,
@@ -31,6 +31,7 @@ export type ViewerLivestreamTopViewProps = {
31
31
  * Component to customize the Follower count indicator on the viewer's live stream's top view.
32
32
  */
33
33
  FollowerCount?: React.ComponentType<FollowerCountProps> | null;
34
+ onLayout?: ViewProps['onLayout'];
34
35
  };
35
36
 
36
37
  /**
@@ -40,6 +41,7 @@ export const ViewerLivestreamTopView = ({
40
41
  DurationBadge = DefaultDurationBadge,
41
42
  LiveIndicator = DefaultLiveIndicator,
42
43
  FollowerCount = DefaultFollowerCount,
44
+ onLayout,
43
45
  }: ViewerLivestreamTopViewProps) => {
44
46
  const {
45
47
  theme: { colors, viewerLivestreamTopView },
@@ -52,6 +54,7 @@ export const ViewerLivestreamTopView = ({
52
54
  { backgroundColor: colors.static_overlay },
53
55
  viewerLivestreamTopView.container,
54
56
  ]}
57
+ onLayout={onLayout}
55
58
  >
56
59
  <View style={[styles.leftElement, viewerLivestreamTopView.leftElement]}>
57
60
  <View style={[styles.liveInfo, viewerLivestreamTopView.liveInfo]}>
@@ -2,3 +2,4 @@ export * from './DurationBadge';
2
2
  export * from './FollowerCount';
3
3
  export * from './LiveIndicator';
4
4
  export * from './HostLivestreamTopView';
5
+ export * from './ViewerLivestreamTopView';