@100mslive/roomkit-react 0.1.8 → 0.1.9-alpha.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.
Files changed (139) hide show
  1. package/dist/{HLSView-DDGPZHA2.js → HLSView-U53QN3AC.js} +3 -3
  2. package/dist/Modal/Dialog.d.ts +402 -1706
  3. package/dist/Prebuilt/App.d.ts +5 -0
  4. package/dist/Prebuilt/AppContext.d.ts +1 -0
  5. package/dist/Prebuilt/AppStateContext.d.ts +16 -0
  6. package/dist/Prebuilt/components/ConferenceScreen.d.ts +2 -0
  7. package/dist/Prebuilt/components/Footer/PollsToggle.d.ts +2 -0
  8. package/dist/Prebuilt/components/LeaveScreen.d.ts +2 -0
  9. package/dist/Prebuilt/components/MwebLandscapePrompt.d.ts +2 -0
  10. package/dist/Prebuilt/components/Notifications/AutoplayBlockedModal.d.ts +2 -0
  11. package/dist/Prebuilt/components/Notifications/HLSFailureModal.d.ts +2 -0
  12. package/dist/Prebuilt/components/Notifications/InitErrorModal.d.ts +2 -0
  13. package/dist/Prebuilt/components/Notifications/Notifications.d.ts +2 -0
  14. package/dist/Prebuilt/components/Notifications/PeerNotifications.d.ts +1 -0
  15. package/dist/Prebuilt/components/Notifications/PermissionErrorModal.d.ts +2 -0
  16. package/dist/Prebuilt/components/Notifications/ReconnectNotifications.d.ts +2 -0
  17. package/dist/Prebuilt/components/Notifications/TrackBulkUnmuteModal.d.ts +2 -0
  18. package/dist/Prebuilt/components/Notifications/TrackNotifications.d.ts +1 -0
  19. package/dist/Prebuilt/components/Notifications/TrackUnmuteModal.d.ts +2 -0
  20. package/dist/Prebuilt/components/Polls/Polls.d.ts +2 -0
  21. package/dist/Prebuilt/components/Preview/PreviewJoin.d.ts +1 -2
  22. package/dist/Prebuilt/components/Preview/PreviewScreen.d.ts +2 -0
  23. package/dist/Prebuilt/components/hooks/useRedirectToLeave.d.ts +1 -1
  24. package/dist/{VirtualBackground-UVZJVOA2.js → VirtualBackground-PMLQPJB6.js} +3 -5
  25. package/dist/{VirtualBackground-UVZJVOA2.js.map → VirtualBackground-PMLQPJB6.js.map} +1 -1
  26. package/dist/chunk-ANQRGVIX.js +14441 -0
  27. package/dist/chunk-ANQRGVIX.js.map +7 -0
  28. package/dist/{chunk-6SQTFOK6.js → chunk-XQ2NRKIW.js} +66 -3
  29. package/dist/{chunk-6SQTFOK6.js.map → chunk-XQ2NRKIW.js.map} +4 -4
  30. package/dist/context/DialogContext.d.ts +6 -0
  31. package/dist/hooks/useDialogContainerSelector.d.ts +1 -0
  32. package/dist/index.cjs.js +10944 -9974
  33. package/dist/index.cjs.js.map +4 -4
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +6 -2
  36. package/dist/meta.cjs.json +3871 -3188
  37. package/dist/meta.esbuild.json +4303 -3728
  38. package/dist/utils/animations.d.ts +11 -0
  39. package/package.json +6 -7
  40. package/src/Modal/Dialog.tsx +31 -3
  41. package/src/Prebuilt/App.tsx +46 -99
  42. package/src/Prebuilt/AppContext.tsx +4 -0
  43. package/src/Prebuilt/AppStateContext.tsx +71 -0
  44. package/src/Prebuilt/common/constants.js +35 -0
  45. package/src/Prebuilt/common/utils.js +47 -0
  46. package/src/Prebuilt/components/AppData/AppData.jsx +5 -0
  47. package/src/Prebuilt/components/AppData/useSidepane.js +23 -1
  48. package/src/Prebuilt/components/AppData/useUISettings.js +48 -4
  49. package/src/Prebuilt/components/{conference.jsx → ConferenceScreen.tsx} +30 -43
  50. package/src/Prebuilt/components/Footer/Footer.tsx +5 -0
  51. package/src/Prebuilt/components/Footer/PaginatedParticipants.tsx +63 -32
  52. package/src/Prebuilt/components/Footer/ParticipantList.jsx +2 -1
  53. package/src/Prebuilt/components/Footer/PollsToggle.tsx +22 -0
  54. package/src/Prebuilt/components/Footer/RoleAccordion.tsx +2 -2
  55. package/src/Prebuilt/components/Header/StreamActions.tsx +5 -3
  56. package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +4 -5
  57. package/src/Prebuilt/components/Leave/LeaveRoom.tsx +0 -4
  58. package/src/Prebuilt/components/{PostLeave.jsx → LeaveScreen.tsx} +6 -13
  59. package/src/Prebuilt/components/MoreSettings/ChangeNameModal.jsx +2 -3
  60. package/src/Prebuilt/components/MoreSettings/EmbedUrl.jsx +2 -3
  61. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +18 -1
  62. package/src/Prebuilt/components/{MwebLandscapePrompt.jsx → MwebLandscapePrompt.tsx} +10 -11
  63. package/src/Prebuilt/components/Notifications/{AutoplayBlockedModal.jsx → AutoplayBlockedModal.tsx} +2 -1
  64. package/src/Prebuilt/components/Notifications/{HLSFailureModal.jsx → HLSFailureModal.tsx} +10 -8
  65. package/src/Prebuilt/components/Notifications/{InitErrorModal.jsx → InitErrorModal.tsx} +5 -2
  66. package/src/Prebuilt/components/Notifications/{Notifications.jsx → Notifications.tsx} +41 -27
  67. package/src/Prebuilt/components/Notifications/{PeerNotifications.jsx → PeerNotifications.tsx} +3 -0
  68. package/src/Prebuilt/components/Notifications/{PermissionErrorModal.jsx → PermissionErrorModal.tsx} +6 -4
  69. package/src/Prebuilt/components/Notifications/{ReconnectNotifications.jsx → ReconnectNotifications.tsx} +11 -6
  70. package/src/Prebuilt/components/Notifications/{TrackBulkUnmuteModal.jsx → TrackBulkUnmuteModal.tsx} +9 -3
  71. package/src/Prebuilt/components/Notifications/{TrackUnmuteModal.jsx → TrackUnmuteModal.tsx} +9 -3
  72. package/src/Prebuilt/components/Notifications/index.tsx +1 -0
  73. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +229 -0
  74. package/src/Prebuilt/components/Polls/CreatePollQuiz/Timer.jsx +71 -0
  75. package/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx +132 -0
  76. package/src/Prebuilt/components/Polls/CreateQuestions/DeleteQuestionModal.jsx +66 -0
  77. package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.jsx +251 -0
  78. package/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.jsx +57 -0
  79. package/src/Prebuilt/components/Polls/Polls.tsx +28 -0
  80. package/src/Prebuilt/components/Polls/Voting/PollResultSummary.jsx +125 -0
  81. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +249 -0
  82. package/src/Prebuilt/components/Polls/Voting/StandardVoting.jsx +40 -0
  83. package/src/Prebuilt/components/Polls/Voting/TimedVoting.jsx +36 -0
  84. package/src/Prebuilt/components/Polls/Voting/Voting.jsx +99 -0
  85. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +101 -0
  86. package/src/Prebuilt/components/Polls/common/OptionInputWithDelete.jsx +25 -0
  87. package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +125 -0
  88. package/src/Prebuilt/components/Polls/common/StatusIndicator.jsx +47 -0
  89. package/src/Prebuilt/components/Polls/common/VoteCount.jsx +28 -0
  90. package/src/Prebuilt/components/Polls/common/VoteProgress.jsx +17 -0
  91. package/src/Prebuilt/components/Polls/common/VoterList.jsx +22 -0
  92. package/src/Prebuilt/components/Polls/common/Votes.jsx +72 -0
  93. package/src/Prebuilt/components/Preview/PreviewForm.tsx +3 -2
  94. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +32 -27
  95. package/src/Prebuilt/components/Preview/{PreviewContainer.tsx → PreviewScreen.tsx} +2 -19
  96. package/src/Prebuilt/components/RaiseHand.jsx +1 -1
  97. package/src/Prebuilt/components/RoleChangeModal.jsx +2 -3
  98. package/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx +2 -3
  99. package/src/Prebuilt/components/Settings/SettingsModal.jsx +2 -3
  100. package/src/Prebuilt/components/Settings/StartRecording.jsx +15 -4
  101. package/src/Prebuilt/components/SidePaneTabs.tsx +1 -1
  102. package/src/Prebuilt/components/StatsForNerds.jsx +2 -3
  103. package/src/Prebuilt/components/Streaming/Common.jsx +31 -21
  104. package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +8 -9
  105. package/src/Prebuilt/components/VideoTile.jsx +37 -33
  106. package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +3 -3
  107. package/src/Prebuilt/components/hooks/useRedirectToLeave.tsx +9 -17
  108. package/src/Prebuilt/components/pdfAnnotator/pdfFileOptions.jsx +2 -3
  109. package/src/Prebuilt/components/pdfAnnotator/submitPdf.jsx +1 -1
  110. package/src/Prebuilt/components/pdfAnnotator/uploadedFile.jsx +2 -3
  111. package/src/Prebuilt/layouts/EmbedView.jsx +47 -60
  112. package/src/Prebuilt/layouts/PDFView.jsx +49 -99
  113. package/src/Prebuilt/layouts/SidePane.tsx +8 -4
  114. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +2 -2
  115. package/src/Prebuilt/primitives/DialogContent.jsx +4 -5
  116. package/src/context/DialogContext.tsx +13 -0
  117. package/src/hooks/useDialogContainerSelector.tsx +7 -0
  118. package/src/index.ts +1 -0
  119. package/src/utils/animations.ts +6 -0
  120. package/dist/Prebuilt/components/Notifications/HeadlessEndRoomListener.d.ts +0 -2
  121. package/dist/Prebuilt/components/PrebuiltDialogPortal.d.ts +0 -4
  122. package/dist/Prebuilt/components/PrebuiltTileElements.d.ts +0 -2198
  123. package/dist/Prebuilt/components/Preview/PreviewContainer.d.ts +0 -3
  124. package/dist/chunk-HUMNPIYI.js +0 -70
  125. package/dist/chunk-HUMNPIYI.js.map +0 -7
  126. package/dist/chunk-PRM33R4R.js +0 -7160
  127. package/dist/chunk-PRM33R4R.js.map +0 -7
  128. package/dist/conference-N7S47TDK.js +0 -6602
  129. package/dist/conference-N7S47TDK.js.map +0 -7
  130. package/src/Prebuilt/components/GoLiveButton.jsx +0 -42
  131. package/src/Prebuilt/components/Notifications/HeadlessEndRoomListener.tsx +0 -23
  132. package/src/Prebuilt/components/PrebuiltDialogPortal.tsx +0 -6
  133. package/src/Prebuilt/components/PrebuiltTileElements.tsx +0 -5
  134. package/src/Prebuilt/components/Streaming/HLSStreaming.jsx +0 -220
  135. package/src/Prebuilt/components/Streaming/RTMPStreaming.jsx +0 -334
  136. package/src/Prebuilt/components/Streaming/StreamingLanding.jsx +0 -76
  137. /package/dist/{HLSView-DDGPZHA2.js.map → HLSView-U53QN3AC.js.map} +0 -0
  138. /package/{src/Prebuilt/components/Notifications/index.jsx → dist/Prebuilt/components/Notifications/index.d.ts} +0 -0
  139. /package/src/Prebuilt/components/Notifications/{TrackNotifications.jsx → TrackNotifications.tsx} +0 -0
@@ -1,42 +0,0 @@
1
- import React from 'react';
2
- import { useRecordingStreaming } from '@100mslive/react-sdk';
3
- import { GoLiveIcon } from '@100mslive/react-icons';
4
- import { Button } from '../../Button';
5
- import { Tooltip } from '../../Tooltip';
6
- import { useIsSidepaneTypeOpen, useSidepaneToggle } from './AppData/useSidepane';
7
- import { useIsHLSStartedFromUI, useIsRTMPStartedFromUI } from './AppData/useUISettings';
8
- import { SIDE_PANE_OPTIONS } from './../common/constants';
9
-
10
- const GoLiveButton = () => {
11
- const isStreamingSidepaneOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.STREAMING);
12
- const toggleStreaming = useSidepaneToggle(SIDE_PANE_OPTIONS.STREAMING);
13
- const { isStreamingOn, isBrowserRecordingOn } = useRecordingStreaming();
14
- const isHLSStartedFromUI = useIsHLSStartedFromUI();
15
- const isRTMPStartedFromUI = useIsRTMPStartedFromUI();
16
- let tooltipText = 'Start streaming';
17
- if (isHLSStartedFromUI || isRTMPStartedFromUI) {
18
- if (isHLSStartedFromUI) {
19
- tooltipText = 'HLS start in progress';
20
- }
21
- if (isRTMPStartedFromUI) {
22
- tooltipText = 'RTMP start in progress';
23
- }
24
- }
25
- return (
26
- <Tooltip title={tooltipText}>
27
- <Button
28
- data-testid="go_live"
29
- variant={isStreamingSidepaneOpen ? 'standard' : 'primary'}
30
- onClick={toggleStreaming}
31
- icon
32
- loading={isRTMPStartedFromUI || isHLSStartedFromUI}
33
- disabled={isBrowserRecordingOn && !isStreamingOn}
34
- >
35
- <GoLiveIcon />
36
- Go Live
37
- </Button>
38
- </Tooltip>
39
- );
40
- };
41
-
42
- export default GoLiveButton;
@@ -1,23 +0,0 @@
1
- import React, { useEffect } from 'react';
2
- import { HMSNotificationTypes, useHMSNotifications } from '@100mslive/react-sdk';
3
- // @ts-ignore
4
- import { useIsNotificationDisabled } from '../AppData/useUISettings';
5
- import { useRedirectToLeave } from '../hooks/useRedirectToLeave';
6
-
7
- export function HeadlessEndRoomListener() {
8
- const notification = useHMSNotifications();
9
- const isNotificationDisabled = useIsNotificationDisabled();
10
- const { redirectToLeave } = useRedirectToLeave();
11
-
12
- useEffect(() => {
13
- if (!notification || !isNotificationDisabled) {
14
- return;
15
- }
16
- if ([HMSNotificationTypes.ROOM_ENDED, HMSNotificationTypes.REMOVED_FROM_ROOM].includes(notification.type)) {
17
- redirectToLeave(1000);
18
- }
19
- // eslint-disable-next-line react-hooks/exhaustive-deps
20
- }, [notification]);
21
-
22
- return <></>;
23
- }
@@ -1,6 +0,0 @@
1
- import React, { ReactNode } from 'react';
2
- import { Dialog } from '../../Modal';
3
-
4
- export const PrebuiltDialogPortal = ({ children }: { children: ReactNode }) => (
5
- <Dialog.Portal container={document.getElementById('prebuilt-container')}>{children}</Dialog.Portal>
6
- );
@@ -1,5 +0,0 @@
1
- import { styled } from '../../Theme';
2
- import { StyledVideoTile } from '../../VideoTile';
3
-
4
- export const PrebuiltAudioIndicator = styled(StyledVideoTile.AudioIndicator, { height: '$12', width: '$12' });
5
- export const PrebuiltAttributeBox = styled(StyledVideoTile.AttributeBox, { height: '$12', width: '$12' });
@@ -1,220 +0,0 @@
1
- import React, { Fragment, useCallback, useEffect, useState } from 'react';
2
- import { selectRoomID, useHMSActions, useHMSStore, useRecordingStreaming } from '@100mslive/react-sdk';
3
- import {
4
- EndStreamIcon,
5
- EyeOpenIcon,
6
- GoLiveIcon,
7
- InfoIcon,
8
- LinkIcon,
9
- PeopleIcon,
10
- SupportIcon,
11
- WrenchIcon,
12
- } from '@100mslive/react-icons';
13
- import { Box, Button, Flex, Loading, Text } from '../../../';
14
- import { Container, ContentBody, ContentHeader, ErrorText, RecordStream } from './Common';
15
- import { useSetAppDataByKey } from '../AppData/useUISettings';
16
- import { useFilteredRoles } from '../../common/hooks';
17
- import { APP_DATA } from '../../common/constants';
18
-
19
- const getCardData = (roleName, roomId) => {
20
- let data = {};
21
- const formattedRoleName = roleName[0].toUpperCase() + roleName.slice(1);
22
-
23
- switch (roleName) {
24
- case 'broadcaster': {
25
- data = {
26
- title: formattedRoleName,
27
- content: 'Broadcasters can livestream audio or video, manage stream appearance and control the room via HLS.',
28
- icon: <SupportIcon />,
29
- };
30
- break;
31
- }
32
- case 'hls-viewer': {
33
- data = {
34
- title: 'HLS Viewer',
35
- content:
36
- 'Viewers can view and send chat messages, but need to be made broadcasters to participate with audio or video.',
37
- icon: <EyeOpenIcon />,
38
- };
39
- break;
40
- }
41
- default:
42
- data = {
43
- title: formattedRoleName,
44
- content: `${formattedRoleName} is customised with specific permissions, which will determine how it interacts with this room.`,
45
- icon: <WrenchIcon />,
46
- order: 1,
47
- };
48
- }
49
- data['link'] = `/${roomId}/${roleName}`;
50
- return data;
51
- };
52
-
53
- const Card = ({ title, icon, link, content, isHLSRunning, order = 0 }) => {
54
- const [copied, setCopied] = useState(false);
55
- return isHLSRunning ? (
56
- <Box
57
- key={title}
58
- css={{
59
- backgroundColor: '$surface_bright',
60
- padding: '$10',
61
- order,
62
- borderRadius: '$2',
63
- }}
64
- >
65
- <Flex align="center" gap="2" css={{ color: '$primary_bright' }}>
66
- {icon}
67
- <Text variant="h6" css={{ fontWeight: '$semiBold' }}>
68
- {title}
69
- </Text>
70
- </Flex>
71
- <Text variant="sm" css={{ color: '$on_surface_medium', mt: '$6' }}>
72
- {content}
73
- </Text>
74
- <Button
75
- variant="standard"
76
- onClick={() => {
77
- navigator.clipboard.writeText(`${window.location.origin}${link}`);
78
- setCopied(true);
79
- setTimeout(() => setCopied(false), 2000);
80
- }}
81
- css={{ w: '100%', r: '$1', mt: '$10', fontWeight: '$semiBold' }}
82
- icon
83
- >
84
- {copied ? (
85
- <>Link copied!</>
86
- ) : (
87
- <>
88
- <LinkIcon style={{ color: 'inherit' }} />
89
- Copy Invite Link
90
- </>
91
- )}
92
- </Button>
93
- </Box>
94
- ) : null;
95
- };
96
-
97
- export const HLSStreaming = ({ onBack }) => {
98
- const roleNames = useFilteredRoles();
99
- const roomId = useHMSStore(selectRoomID);
100
- const cards = roleNames.map(roleName => getCardData(roleName, roomId));
101
-
102
- const { isHLSRunning } = useRecordingStreaming();
103
- const [showLinks, setShowLinks] = useState(false);
104
- return !showLinks ? (
105
- <Container rounded>
106
- <ContentHeader title="Start Streaming" content="HLS" onBack={onBack} />
107
- <ContentBody title="HLS Streaming" Icon={GoLiveIcon} removeVerticalPadding>
108
- Stream directly from the browser using any device with multiple hosts and real-time messaging, all within this
109
- platform.
110
- </ContentBody>
111
- {isHLSRunning ? <EndHLS setShowLinks={setShowLinks} /> : <StartHLS />}
112
- </Container>
113
- ) : (
114
- <Container rounded>
115
- <ContentHeader title="Invite People" content="Start the conversation" onBack={() => setShowLinks(false)} />
116
-
117
- <Flex direction="column" css={{ gap: '$10', p: '$0 $10', overflowY: 'auto', mb: '$10' }}>
118
- {cards.map(card => (
119
- <Card key={card.title} {...card} isHLSRunning={isHLSRunning} />
120
- ))}
121
- </Flex>
122
- </Container>
123
- );
124
- };
125
-
126
- const StartHLS = () => {
127
- const [record, setRecord] = useState(false);
128
- const [error, setError] = useState(false);
129
- const hmsActions = useHMSActions();
130
- const [isHLSStarted, setHLSStarted] = useSetAppDataByKey(APP_DATA.hlsStarted);
131
- const startHLS = useCallback(
132
- async variants => {
133
- try {
134
- if (isHLSStarted) {
135
- return;
136
- }
137
- setHLSStarted(true);
138
- setError('');
139
- await hmsActions.startHLSStreaming({
140
- variants,
141
- recording: { hlsVod: record, singleFilePerLayer: record },
142
- });
143
- } catch (error) {
144
- setHLSStarted(false);
145
- setError(error.message);
146
- }
147
- },
148
- [hmsActions, record, isHLSStarted, setHLSStarted],
149
- );
150
-
151
- return (
152
- <Fragment>
153
- <RecordStream record={record} setRecord={setRecord} testId="hls-recording" />
154
- <Box css={{ p: '$4 $10' }}>
155
- <ErrorText error={error} />
156
- <Button
157
- data-testid="start_hls"
158
- css={{ w: '100%', r: '$0' }}
159
- icon
160
- onClick={() => startHLS()}
161
- disabled={isHLSStarted}
162
- >
163
- {isHLSStarted ? <Loading size={24} color="currentColor" /> : <GoLiveIcon />}
164
- {isHLSStarted ? 'Starting stream...' : 'Go Live'}
165
- </Button>
166
- </Box>
167
- <Flex align="center" css={{ p: '$4 $10' }}>
168
- <Text>
169
- <InfoIcon width={16} height={16} />
170
- </Text>
171
- <Text variant="tiny" color="$on_surface_medium" css={{ mx: '$8' }}>
172
- You cannot start recording once the stream starts, you will have to stop the stream to enable recording.
173
- </Text>
174
- </Flex>
175
- </Fragment>
176
- );
177
- };
178
-
179
- const EndHLS = ({ setShowLinks }) => {
180
- const hmsActions = useHMSActions();
181
-
182
- const [inProgress, setInProgress] = useState(false);
183
- const [error, setError] = useState('');
184
- const { isHLSRunning } = useRecordingStreaming();
185
-
186
- useEffect(() => {
187
- if (inProgress && !isHLSRunning) {
188
- setInProgress(false);
189
- }
190
- }, [inProgress, isHLSRunning]);
191
-
192
- return (
193
- <Box css={{ p: '$4 $10' }}>
194
- <ErrorText error={error} />
195
- <Button
196
- data-testid="stop_hls"
197
- variant="danger"
198
- css={{ w: '100%', r: '$0', mt: '$8' }}
199
- icon
200
- loading={inProgress}
201
- disabled={inProgress}
202
- onClick={async () => {
203
- try {
204
- setInProgress(true);
205
- await hmsActions.stopHLSStreaming();
206
- } catch (error) {
207
- setError(error.message);
208
- setInProgress(false);
209
- }
210
- }}
211
- >
212
- <EndStreamIcon />
213
- End Stream
214
- </Button>
215
- <Button icon css={{ w: '100%', r: '$0', mt: '$8' }} onClick={() => setShowLinks(true)}>
216
- <PeopleIcon /> Invite People
217
- </Button>
218
- </Box>
219
- );
220
- };
@@ -1,334 +0,0 @@
1
- import React, { useEffect, useRef, useState } from 'react';
2
- import { useHMSActions, useRecordingStreaming } from '@100mslive/react-sdk';
3
- import { AddCircleIcon, EndStreamIcon, GoLiveIcon, PencilIcon, SettingsIcon, TrashIcon } from '@100mslive/react-icons';
4
- import { Accordion, Box, Button, Flex, Input, Label, Loading, Text } from '../../../';
5
- import { Container, ContentBody, ContentHeader, ErrorText, RecordStream } from './Common';
6
- import { ResolutionInput } from './ResolutionInput';
7
- import { useSetAppDataByKey } from '../AppData/useUISettings';
8
- import { UserPreferencesKeys, useUserPreferences } from '../hooks/useUserPreferences';
9
- import { APP_DATA, RTMP_RECORD_DEFAULT_RESOLUTION } from '../../common/constants';
10
-
11
- export const RTMPStreaming = ({ onBack }) => {
12
- const { isRTMPRunning } = useRecordingStreaming();
13
-
14
- return (
15
- <Container>
16
- <ContentHeader title="Start Streaming" content="Choose a destination" onBack={onBack} />
17
- <ContentBody Icon={SettingsIcon} title="RTMP">
18
- Live Stream your call to Twitch, YouTube, Facebook and any app which supports RTMP, all at the same time
19
- </ContentBody>
20
- {!isRTMPRunning ? <StartRTMP /> : <EndRTMP />}
21
- </Container>
22
- );
23
- };
24
-
25
- const StartRTMP = () => {
26
- const [rtmpPreference = [], setRTMPPreference] = useUserPreferences(UserPreferencesKeys.RTMP_URLS);
27
- const [rtmpStreams, setRTMPStreams] = useState(
28
- rtmpPreference.length > 0
29
- ? rtmpPreference
30
- : [
31
- {
32
- name: 'Stream',
33
- id: Date.now(),
34
- rtmpURL: '',
35
- streamKey: '',
36
- },
37
- ],
38
- );
39
- const hmsActions = useHMSActions();
40
- const [error, setError] = useState(false);
41
- const [record, setRecord] = useState(false);
42
- const [resolution, setResolution] = useState(RTMP_RECORD_DEFAULT_RESOLUTION);
43
- const [isRTMPStarted, setRTMPStarted] = useSetAppDataByKey(APP_DATA.rtmpStarted);
44
- const hasRTMPURL = rtmpStreams.some(value => value.rtmpURL && value.streamKey);
45
-
46
- return (
47
- <Box
48
- css={{ overflowY: 'auto' }}
49
- as="form"
50
- onSubmit={e => {
51
- e.preventDefault();
52
- }}
53
- >
54
- {rtmpStreams.length > 0 && (
55
- <Box css={{ px: '$10' }}>
56
- <Accordion.Root type="single" collapsible defaultValue={rtmpStreams[0].id}>
57
- {rtmpStreams.map((rtmp, index) => {
58
- return (
59
- <Accordion.Item
60
- value={rtmp.id}
61
- key={rtmp.id}
62
- css={{
63
- border: '2px solid $surface_bright !important',
64
- r: '$1',
65
- my: '$4',
66
- }}
67
- >
68
- <AccordionHeader rtmp={rtmp} setRTMPStreams={setRTMPStreams} />
69
- <Accordion.Content css={{ px: '$8', py: 0 }}>
70
- <RTMPForm {...rtmp} setRTMPStreams={setRTMPStreams} testId={`${index}_rtmp`} />
71
- </Accordion.Content>
72
- </Accordion.Item>
73
- );
74
- })}
75
- </Accordion.Root>
76
- </Box>
77
- )}
78
- <ResolutionInput
79
- testId="rtmp_resolution"
80
- onResolutionChange={setResolution}
81
- css={{
82
- flexDirection: 'column',
83
- alignItems: 'start',
84
- px: '$10',
85
- my: '$8',
86
- }}
87
- />
88
- <RecordStream record={record} setRecord={setRecord} testId="rtmp_recording" />
89
- <Box css={{ p: '$8 $10', '@lg': { display: 'flex', gap: '$4' } }}>
90
- {rtmpStreams.length < 3 && (
91
- <Button
92
- data-testid="add_stream"
93
- variant="standard"
94
- outlined
95
- icon
96
- css={{ my: '$4', w: '100%' }}
97
- onClick={() => {
98
- setRTMPStreams(streams => [
99
- ...streams,
100
- {
101
- name: 'Stream',
102
- id: Date.now(),
103
- rtmpURL: '',
104
- streamKey: '',
105
- },
106
- ]);
107
- }}
108
- >
109
- <AddCircleIcon /> Add Stream
110
- </Button>
111
- )}
112
-
113
- <Button
114
- data-testid="start_rtmp"
115
- variant="primary"
116
- icon
117
- type="submit"
118
- css={{ w: '100%', my: '$4' }}
119
- disabled={isRTMPStarted || (rtmpStreams.length === 0 && !record)}
120
- onClick={async () => {
121
- try {
122
- const hasInvalidData = rtmpStreams.find(
123
- value => (value.rtmpURL && !value.streamKey) || (value.streamKey && !value.rtmpURL),
124
- );
125
- if (hasInvalidData || (rtmpStreams.length > 0 && !hasRTMPURL)) {
126
- return;
127
- }
128
- setError('');
129
- setRTMPStarted(true);
130
- const urls = hasRTMPURL ? rtmpStreams.map(value => `${value.rtmpURL}/${value.streamKey}`) : [];
131
- await hmsActions.startRTMPOrRecording({
132
- rtmpURLs: urls,
133
- resolution: getResolution(resolution),
134
- record: record,
135
- });
136
- setRTMPPreference(rtmpStreams);
137
- } catch (error) {
138
- console.error(error);
139
- setError(error.message);
140
- setRTMPStarted(false);
141
- }
142
- }}
143
- >
144
- {isRTMPStarted ? <Loading size={24} color="currentColor" /> : <GoLiveIcon />}
145
- {isRTMPStarted ? 'Starting stream...' : 'Go Live'}
146
- </Button>
147
- <ErrorText error={error} />
148
- </Box>
149
- </Box>
150
- );
151
- };
152
-
153
- const EndRTMP = () => {
154
- const hmsActions = useHMSActions();
155
- const [inProgress, setInProgress] = useState(false);
156
- const [error, setError] = useState('');
157
- const { isRTMPRunning } = useRecordingStreaming();
158
-
159
- useEffect(() => {
160
- if (inProgress && !isRTMPRunning) {
161
- setInProgress(false);
162
- }
163
- }, [inProgress, isRTMPRunning]);
164
-
165
- return (
166
- <Box css={{ p: '$4 $10' }}>
167
- <ErrorText error={error} />
168
- <Button
169
- data-testid="stop_rtmp"
170
- variant="danger"
171
- css={{ w: '100%', r: '$0', my: '$8' }}
172
- icon
173
- loading={inProgress}
174
- disabled={inProgress}
175
- onClick={async () => {
176
- try {
177
- setInProgress(true);
178
- await hmsActions.stopRTMPAndRecording();
179
- } catch (error) {
180
- setError(error.message);
181
- setInProgress(false);
182
- }
183
- }}
184
- >
185
- <EndStreamIcon />
186
- End Stream
187
- </Button>
188
- </Box>
189
- );
190
- };
191
-
192
- const ActionIcon = ({ icon: Icon, onClick }) => {
193
- return (
194
- <Text as="span" css={{ mx: '$2', cursor: 'pointer' }} onClick={onClick}>
195
- <Icon width={16} height={16} />
196
- </Text>
197
- );
198
- };
199
-
200
- const FormLabel = ({ id, children }) => {
201
- return (
202
- <Label htmlFor={id} css={{ color: '$on_surface_high', my: '$4', fontSize: '$sm' }}>
203
- {children}
204
- </Label>
205
- );
206
- };
207
-
208
- const RTMPForm = ({ rtmpURL, id, streamKey, setRTMPStreams, testId }) => {
209
- const formRef = useRef(null);
210
- return (
211
- <Flex id={id} direction="column" css={{ mb: '$8', px: '$8' }} ref={formRef}>
212
- <FormLabel id="rtmpURL">
213
- RTMP URL
214
- <Asterik />
215
- </FormLabel>
216
- <Input
217
- data-testid={`${testId}_url`}
218
- placeholder="Enter RTMP URL"
219
- id="rtmpURL"
220
- name="rtmpURL"
221
- value={rtmpURL}
222
- onChange={e => {
223
- setRTMPStreams(streams =>
224
- updateStream({
225
- streams,
226
- id,
227
- value: e.target.value,
228
- key: e.target.name,
229
- }),
230
- );
231
- }}
232
- required
233
- />
234
- <FormLabel id="streamKey">
235
- Stream Key
236
- <Asterik />
237
- </FormLabel>
238
- <Input
239
- placeholder="Enter Stream Key"
240
- id="streamKey"
241
- name="streamKey"
242
- value={streamKey}
243
- data-testid={`${testId}_key`}
244
- onChange={e => {
245
- setRTMPStreams(streams =>
246
- updateStream({
247
- streams,
248
- id,
249
- value: e.target.value,
250
- key: e.target.name,
251
- }),
252
- );
253
- }}
254
- required
255
- />
256
- </Flex>
257
- );
258
- };
259
-
260
- const Asterik = () => {
261
- return (
262
- <Text variant="sm" as="span" css={{ color: '$alert_error_default', mx: '$2' }}>
263
- *
264
- </Text>
265
- );
266
- };
267
- const AccordionHeader = ({ rtmp, setRTMPStreams }) => {
268
- const [edit, setEdit] = useState(false);
269
- return (
270
- <Accordion.Header css={{ px: '$8' }}>
271
- {edit ? (
272
- <Input
273
- defaultValue={rtmp.name}
274
- autoFocus
275
- onBlur={e => {
276
- const value = e.currentTarget.value.trim();
277
- if (value) {
278
- setRTMPStreams(streams =>
279
- streams.map(stream => {
280
- if (stream.id === rtmp.id) {
281
- stream.name = value;
282
- }
283
- return stream;
284
- }),
285
- );
286
- setEdit(false);
287
- }
288
- }}
289
- />
290
- ) : (
291
- <Text css={{ flex: '1 1 0' }}>{rtmp.name}</Text>
292
- )}
293
- <Flex css={{ mx: '$4', gap: '$2' }}>
294
- <ActionIcon
295
- onClick={e => {
296
- e.stopPropagation();
297
- setEdit(true);
298
- }}
299
- icon={PencilIcon}
300
- />
301
- <ActionIcon
302
- onClick={() => {
303
- setRTMPStreams(streams => streams.filter(stream => stream.id !== rtmp.id));
304
- }}
305
- icon={TrashIcon}
306
- />
307
- </Flex>
308
- </Accordion.Header>
309
- );
310
- };
311
-
312
- const updateStream = ({ streams, id, key, value }) =>
313
- streams.map(stream => {
314
- if (stream.id === id) {
315
- return {
316
- ...stream,
317
- [key]: value,
318
- };
319
- }
320
- return stream;
321
- });
322
-
323
- export function getResolution(recordingResolution) {
324
- const resolution = {};
325
- if (recordingResolution.width) {
326
- resolution.width = recordingResolution.width;
327
- }
328
- if (recordingResolution.height) {
329
- resolution.height = recordingResolution.height;
330
- }
331
- if (Object.keys(resolution).length > 0) {
332
- return resolution;
333
- }
334
- }