@100mslive/roomkit-react 0.3.10 → 0.3.11-alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. package/dist/{HLSView-PF44ZBXN.js → HLSView-77OCJRUH.js} +18 -3
  2. package/dist/HLSView-77OCJRUH.js.map +7 -0
  3. package/dist/{HLSView-6J5FD3IV.css → HLSView-ZFVXF26F.css} +3 -3
  4. package/dist/{HLSView-6J5FD3IV.css.map → HLSView-ZFVXF26F.css.map} +1 -1
  5. package/dist/Prebuilt/common/constants.d.ts +0 -2
  6. package/dist/Prebuilt/common/hooks.d.ts +8 -1
  7. package/dist/Prebuilt/components/MoreSettings/CaptionContent.d.ts +5 -0
  8. package/dist/Prebuilt/components/MoreSettings/CaptionModal.d.ts +4 -0
  9. package/dist/Prebuilt/components/Polls/Voting/StandardVoting.d.ts +4 -2
  10. package/dist/Prebuilt/components/Polls/Voting/TimedVoting.d.ts +4 -2
  11. package/dist/Prebuilt/layouts/WaitingView.d.ts +6 -0
  12. package/dist/{chunk-TUSCTU6T.js → chunk-4X3AES3T.js} +2095 -1805
  13. package/dist/chunk-4X3AES3T.js.map +7 -0
  14. package/dist/index.cjs.css +2 -2
  15. package/dist/index.cjs.css.map +1 -1
  16. package/dist/index.cjs.js +2752 -2446
  17. package/dist/index.cjs.js.map +4 -4
  18. package/dist/index.css +2 -2
  19. package/dist/index.css.map +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/meta.cjs.json +292 -114
  22. package/dist/meta.esbuild.json +311 -132
  23. package/package.json +7 -7
  24. package/src/Prebuilt/common/constants.ts +0 -2
  25. package/src/Prebuilt/common/hooks.ts +34 -1
  26. package/src/Prebuilt/common/utils.js +11 -11
  27. package/src/Prebuilt/components/AppData/AppData.tsx +2 -4
  28. package/src/Prebuilt/components/AppData/useUISettings.js +0 -3
  29. package/src/Prebuilt/components/Chat/Chat.tsx +26 -6
  30. package/src/Prebuilt/components/Chat/ChatFooter.tsx +18 -2
  31. package/src/Prebuilt/components/Chat/ChatStates.tsx +1 -1
  32. package/src/Prebuilt/components/Footer/ChatToggle.tsx +5 -1
  33. package/src/Prebuilt/components/Footer/ParticipantList.tsx +4 -2
  34. package/src/Prebuilt/components/Footer/PollsToggle.tsx +1 -1
  35. package/src/Prebuilt/components/MoreSettings/CaptionContent.tsx +132 -0
  36. package/src/Prebuilt/components/MoreSettings/CaptionModal.tsx +37 -0
  37. package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +40 -3
  38. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +19 -19
  39. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.tsx +2 -15
  40. package/src/Prebuilt/components/Polls/Voting/LeaderboardSummary.tsx +71 -66
  41. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +39 -40
  42. package/src/Prebuilt/components/Polls/Voting/StandardVoting.tsx +12 -6
  43. package/src/Prebuilt/components/Polls/Voting/TimedVoting.tsx +21 -10
  44. package/src/Prebuilt/components/Polls/Voting/Voting.tsx +44 -2
  45. package/src/Prebuilt/components/VideoLayouts/EqualProminence.tsx +13 -17
  46. package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +17 -0
  47. package/src/Prebuilt/layouts/HLSView.jsx +14 -11
  48. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +43 -9
  49. package/src/Prebuilt/layouts/WaitingView.tsx +52 -0
  50. package/dist/HLSView-PF44ZBXN.js.map +0 -7
  51. package/dist/chunk-TUSCTU6T.js.map +0 -7
  52. package/src/Prebuilt/layouts/NonPublisherView.jsx +0 -51
  53. package/src/Prebuilt/layouts/WaitingView.jsx +0 -51
@@ -29,7 +29,7 @@ import {
29
29
  SettingsIcon,
30
30
  VirtualBackgroundIcon,
31
31
  } from '@100mslive/react-icons';
32
- import { Box, Loading, Tooltip } from '../../../..';
32
+ import { Box, Loading, Text, Tooltip } from '../../../..';
33
33
  import { Sheet } from '../../../../Sheet';
34
34
  // @ts-ignore: No implicit any
35
35
  import IconButton from '../../../IconButton';
@@ -43,6 +43,7 @@ import SettingsModal from '../../Settings/SettingsModal';
43
43
  import { ToastManager } from '../../Toast/ToastManager';
44
44
  // @ts-ignore: No implicit any
45
45
  import { ActionTile } from '../ActionTile';
46
+ import { CaptionModal } from '../CaptionModal';
46
47
  // @ts-ignore: No implicit any
47
48
  import { ChangeNameModal } from '../ChangeNameModal';
48
49
  // @ts-ignore: No implicit any
@@ -73,6 +74,7 @@ const MODALS = {
73
74
  BULK_ROLE_CHANGE: 'bulkRoleChange',
74
75
  MUTE_ALL: 'muteAll',
75
76
  EMBED_URL: 'embedUrl',
77
+ CAPTION: 'caption',
76
78
  };
77
79
 
78
80
  export const MwebOptions = ({
@@ -106,9 +108,9 @@ export const MwebOptions = ({
106
108
  const isLocalVideoEnabled = useHMSStore(selectIsLocalVideoEnabled);
107
109
  const { startRecording, isRecordingLoading } = useRecordingHandler();
108
110
 
109
- const isCaptionPresent = useHMSStore(selectIsTranscriptionEnabled);
111
+ const isTranscriptionEnabled = useHMSStore(selectIsTranscriptionEnabled);
110
112
 
111
- const [isCaptionEnabled, setIsCaptionEnabled] = useSetIsCaptionEnabled();
113
+ const [isCaptionEnabled] = useSetIsCaptionEnabled();
112
114
  useDropdownList({ open: openModals.size > 0 || openOptionsSheet || openSettingsSheet, name: 'MoreSettings' });
113
115
 
114
116
  const updateState = (modalName: string, value: boolean) => {
@@ -193,21 +195,17 @@ export const MwebOptions = ({
193
195
  <ActionTile.Title>{isHandRaised ? 'Lower' : 'Raise'} Hand</ActionTile.Title>
194
196
  </ActionTile.Root>
195
197
  ) : null}
196
- {isCaptionPresent && screenType !== 'hls_live_streaming' ? (
197
- <ActionTile.Root
198
- onClick={() => {
199
- setIsCaptionEnabled(!isCaptionEnabled);
200
- }}
201
- >
202
- {isCaptionEnabled ? (
203
- <ClosedCaptionIcon width="20" height="20px" />
204
- ) : (
205
- <OpenCaptionIcon width="20" height="20px" />
206
- )}
207
- <ActionTile.Title>{isCaptionEnabled ? 'Hide Captions' : 'Captions Disabled'}</ActionTile.Title>
208
- </ActionTile.Root>
209
- ) : null}
210
-
198
+ <ActionTile.Root
199
+ onClick={() => {
200
+ setOpenOptionsSheet(false);
201
+ updateState(MODALS.CAPTION, true);
202
+ }}
203
+ >
204
+ {isTranscriptionEnabled && isCaptionEnabled ? <ClosedCaptionIcon /> : <OpenCaptionIcon />}
205
+ <Text variant="sm" css={{ ml: '$4', color: '$on_surface_high', flexGrow: '1' }}>
206
+ Closed Caption
207
+ </Text>
208
+ </ActionTile.Root>
211
209
  {isLocalVideoEnabled && !!elements?.virtual_background ? (
212
210
  <ActionTile.Root
213
211
  onClick={() => {
@@ -323,7 +321,9 @@ export const MwebOptions = ({
323
321
  openParentSheet={() => setOpenOptionsSheet(true)}
324
322
  />
325
323
  )}
326
-
324
+ {openModals.has(MODALS.CAPTION) && (
325
+ <CaptionModal onOpenChange={(value: boolean) => updateState(MODALS.CAPTION, value)} />
326
+ )}
327
327
  {showEmojiCard && (
328
328
  <Box
329
329
  ref={emojiCardRef}
@@ -152,18 +152,6 @@ const AddMenu = () => {
152
152
  Hide Vote Count
153
153
  </Text>
154
154
  </Flex>
155
- {/* <Flex align="center" css={{ mt: '$10' }}>
156
- <Switch onCheckedChange={value => setAnonymous(value)} css={{ mr: '$6' }} />
157
- <Text variant="body2" css={{ c: '$on_surface_medium' }}>
158
- Make Results Anonymous
159
- </Text>
160
- </Flex> */}
161
- {/* <Timer
162
- timer={timer}
163
- setTimer={setTimer}
164
- showTimerDropDown={showTimerDropDown}
165
- setShowTimerDropDown={setShowTimerDropDown}
166
- /> */}
167
155
 
168
156
  <Button
169
157
  variant="primary"
@@ -198,8 +186,8 @@ const PrevMenu = () => {
198
186
  const sortedPolls = useMemo(
199
187
  () =>
200
188
  polls
201
- ?.sort((a, b) => (b.createdAt?.getTime() || 0) - (a.createdAt?.getTime() || 0))
202
- ?.sort((a, b) => (b.state === 'started' ? 1 : 0) - (a.state === 'started' ? 1 : 0)),
189
+ ?.sort((a, b) => (b?.createdAt?.getTime?.() || 0) - (a?.createdAt?.getTime?.() || 0))
190
+ ?.sort((a, b) => (b?.state === 'started' ? 1 : 0) - (a?.state === 'started' ? 1 : 0)),
203
191
  [polls],
204
192
  );
205
193
  const permissions = useHMSStore(selectPermissions);
@@ -208,7 +196,6 @@ const PrevMenu = () => {
208
196
  const updatePolls = async () => {
209
197
  await hmsActions.interactivityCenter.getPolls();
210
198
  };
211
-
212
199
  updatePolls();
213
200
  }, [hmsActions.interactivityCenter]);
214
201
 
@@ -4,6 +4,8 @@ import { ChevronLeftIcon, ChevronRightIcon, CrossIcon } from '@100mslive/react-i
4
4
  import { Box, Flex } from '../../../../Layout';
5
5
  import { Loading } from '../../../../Loading';
6
6
  import { Text } from '../../../../Text';
7
+ // @ts-ignore
8
+ import { Container } from '../../Streaming/Common';
7
9
  import { LeaderboardEntry } from './LeaderboardEntry';
8
10
  import { PeerParticipationSummary } from './PeerParticipationSummary';
9
11
  // @ts-ignore
@@ -32,80 +34,83 @@ export const LeaderboardSummary = ({ pollID }: { pollID: string }) => {
32
34
  const questionCount = quiz.questions?.length || 0;
33
35
 
34
36
  return (
35
- <Flex direction="column" css={{ size: '100%' }}>
36
- <Flex justify="between" align="center" css={{ pb: '$6', borderBottom: '1px solid $border_bright', mb: '$8' }}>
37
- <Flex align="center" css={{ gap: '$4' }}>
37
+ <Container rounded>
38
+ <Flex direction="column" css={{ size: '100%', p: '$8' }}>
39
+ <Flex justify="between" align="center" css={{ pb: '$6', borderBottom: '1px solid $border_bright', mb: '$8' }}>
40
+ <Flex align="center" css={{ gap: '$4' }}>
41
+ <Flex
42
+ css={{ color: '$on_surface_medium', '&:hover': { color: '$on_surface_high', cursor: 'pointer' } }}
43
+ onClick={() => setPollView(POLL_VIEWS.VOTE)}
44
+ >
45
+ <ChevronLeftIcon />
46
+ </Flex>
47
+ <Text variant="lg" css={{ fontWeight: '$semiBold' }}>
48
+ {quiz.title}
49
+ </Text>
50
+ <StatusIndicator status={quiz.state} />
51
+ </Flex>
38
52
  <Flex
39
53
  css={{ color: '$on_surface_medium', '&:hover': { color: '$on_surface_high', cursor: 'pointer' } }}
40
- onClick={() => setPollView(POLL_VIEWS.VOTE)}
54
+ onClick={toggleSidepane}
41
55
  >
42
- <ChevronLeftIcon />
56
+ <CrossIcon />
43
57
  </Flex>
44
- <Text variant="lg" css={{ fontWeight: '$semiBold' }}>
45
- {quiz.title}
46
- </Text>
47
- <StatusIndicator status={quiz.state} />
48
58
  </Flex>
49
- <Flex
50
- css={{ color: '$on_surface_medium', '&:hover': { color: '$on_surface_high', cursor: 'pointer' } }}
51
- onClick={toggleSidepane}
52
- >
53
- <CrossIcon />
54
- </Flex>
55
- </Flex>
56
-
57
- {!viewAllEntries ? <PeerParticipationSummary quiz={quiz} /> : null}
59
+ <Box css={{ overflowY: 'auto', mr: '-$4', pr: '$4' }}>
60
+ {!viewAllEntries ? <PeerParticipationSummary quiz={quiz} /> : null}
58
61
 
59
- <Text variant="sm" css={{ fontWeight: '$semiBold', mt: '$4' }}>
60
- Leaderboard
61
- </Text>
62
- <Text variant="xs" css={{ color: '$on_surface_medium' }}>
63
- Based on score and time taken to cast the correct answer
64
- </Text>
65
- <Box
66
- css={{
67
- mt: '$8',
68
- overflowY: 'auto',
69
- flex: viewAllEntries ? '1 1 0' : 'unset',
70
- mr: viewAllEntries ? '-$6' : 'unset',
71
- px: viewAllEntries ? '0' : '$4',
72
- pr: viewAllEntries ? '$6' : '$4',
73
- backgroundColor: viewAllEntries ? '' : '$surface_default',
74
- borderRadius: '$1',
75
- }}
76
- >
77
- {quizLeaderboard?.entries &&
78
- quizLeaderboard.entries
79
- .slice(0, viewAllEntries ? undefined : 5)
80
- .map(question => (
81
- <LeaderboardEntry
82
- key={question.position}
83
- position={question.position}
84
- score={question.score}
85
- questionCount={questionCount}
86
- correctResponses={question.correctResponses}
87
- userName={question.peer.username || ''}
88
- maxPossibleScore={maxPossibleScore}
89
- duration={question.duration}
90
- />
91
- ))}
92
- {quizLeaderboard?.entries?.length > 5 && !viewAllEntries ? (
93
- <Flex
94
- align="center"
95
- justify="end"
62
+ <Text variant="sm" css={{ fontWeight: '$semiBold', mt: '$4' }}>
63
+ Leaderboard
64
+ </Text>
65
+ <Text variant="xs" css={{ color: '$on_surface_medium' }}>
66
+ Based on score and time taken to cast the correct answer
67
+ </Text>
68
+ <Box
96
69
  css={{
97
- w: '100%',
98
- borderTop: '1px solid $border_bright',
99
- cursor: 'pointer',
100
- color: '$on_surface_high',
101
- p: '$6 $2',
70
+ mt: '$8',
71
+ overflowY: 'auto',
72
+ flex: viewAllEntries ? '1 1 0' : 'unset',
73
+ mr: viewAllEntries ? '-$6' : 'unset',
74
+ px: viewAllEntries ? '0' : '$4',
75
+ pr: viewAllEntries ? '$6' : '$4',
76
+ backgroundColor: viewAllEntries ? '' : '$surface_default',
77
+ borderRadius: '$1',
102
78
  }}
103
- onClick={() => setViewAllEntries(true)}
104
79
  >
105
- <Text variant="sm">View All</Text> <ChevronRightIcon />
106
- </Flex>
107
- ) : null}
108
- </Box>
109
- </Flex>
80
+ {quizLeaderboard?.entries &&
81
+ quizLeaderboard.entries
82
+ .slice(0, viewAllEntries ? undefined : 5)
83
+ .map(question => (
84
+ <LeaderboardEntry
85
+ key={question.position}
86
+ position={question.position}
87
+ score={question.score}
88
+ questionCount={questionCount}
89
+ correctResponses={question.correctResponses}
90
+ userName={question.peer.username || ''}
91
+ maxPossibleScore={maxPossibleScore}
92
+ duration={question.duration}
93
+ />
94
+ ))}
95
+ {quizLeaderboard?.entries?.length > 5 && !viewAllEntries ? (
96
+ <Flex
97
+ align="center"
98
+ justify="end"
99
+ css={{
100
+ w: '100%',
101
+ borderTop: '1px solid $border_bright',
102
+ cursor: 'pointer',
103
+ color: '$on_surface_high',
104
+ p: '$6 $2',
105
+ }}
106
+ onClick={() => setViewAllEntries(true)}
107
+ >
108
+ <Text variant="sm">View All</Text> <ChevronRightIcon />
109
+ </Flex>
110
+ ) : null}
111
+ </Box>
112
+ </Box>
113
+ </Flex>
114
+ </Container>
110
115
  );
111
116
  };
@@ -1,5 +1,5 @@
1
1
  // @ts-check
2
- import React, { useCallback, useMemo, useRef, useState } from 'react';
2
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { match } from 'ts-pattern';
4
4
  import { selectLocalPeer, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
5
5
  import { CheckCircleIcon, ChevronDownIcon, CrossCircleIcon } from '@100mslive/react-icons';
@@ -21,15 +21,12 @@ export const QuestionCard = ({
21
21
  text,
22
22
  options = [],
23
23
  answer,
24
- setCurrentIndex,
25
- responses = [],
24
+ localPeerResponse,
25
+ updateSavedResponses,
26
26
  rolesThatCanViewResponses,
27
27
  }) => {
28
28
  const actions = useHMSActions();
29
29
  const localPeer = useHMSStore(selectLocalPeer);
30
- const localPeerResponse = responses?.find(
31
- response => response.peer?.peerid === localPeer?.id || response.peer?.userid === localPeer?.customerUserId,
32
- );
33
30
 
34
31
  const isLocalPeerCreator = localPeer?.id === startedBy;
35
32
  const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
@@ -37,20 +34,26 @@ export const QuestionCard = ({
37
34
  !rolesThatCanViewResponses ||
38
35
  rolesThatCanViewResponses.length === 0 ||
39
36
  rolesThatCanViewResponses.includes(localPeerRoleName || '');
37
+ const [localPeerChoice, setLocalPeerChoice] = useState(localPeerResponse);
38
+
39
+ useEffect(() => {
40
+ setLocalPeerChoice(localPeerResponse);
41
+ }, [localPeerResponse]);
42
+
40
43
  const showVoteCount =
41
- roleCanViewResponse && (localPeerResponse || (isLocalPeerCreator && pollState === 'stopped')) && !isQuiz;
44
+ roleCanViewResponse && (localPeerChoice || (isLocalPeerCreator && pollState === 'stopped')) && !isQuiz;
42
45
 
43
46
  const isLive = pollState === 'started';
44
47
  const pollEnded = pollState === 'stopped';
45
- const canRespond = isLive && !localPeerResponse;
48
+ const canRespond = isLive && !localPeerChoice;
46
49
  const startTime = useRef(Date.now());
47
- const isCorrectAnswer = checkCorrectAnswer(answer, localPeerResponse, type);
50
+ const isCorrectAnswer = checkCorrectAnswer(answer, localPeerChoice, type);
48
51
 
49
52
  const [singleOptionAnswer, setSingleOptionAnswer] = useState();
50
53
  const [multipleOptionAnswer, setMultipleOptionAnswer] = useState(new Set());
51
54
  const [showOptions, setShowOptions] = useState(true);
52
55
 
53
- const respondedToQuiz = isQuiz && localPeerResponse && !localPeerResponse.skipped;
56
+ const respondedToQuiz = isQuiz && localPeerChoice && !localPeerChoice.skipped;
54
57
 
55
58
  const isValidVote = useMemo(() => {
56
59
  if (type === QUESTION_TYPE.SINGLE_CHOICE) {
@@ -64,17 +67,28 @@ export const QuestionCard = ({
64
67
  if (!isValidVote) {
65
68
  return;
66
69
  }
67
-
68
- await actions.interactivityCenter.addResponsesToPoll(pollID, [
69
- {
70
- questionIndex: index,
71
- option: singleOptionAnswer,
72
- options: Array.from(multipleOptionAnswer),
73
- duration: Date.now() - startTime.current,
74
- },
75
- ]);
70
+ const submittedResponse = {
71
+ questionIndex: index,
72
+ option: singleOptionAnswer,
73
+ options: Array.from(multipleOptionAnswer),
74
+ duration: Date.now() - startTime.current,
75
+ };
76
+ await actions.interactivityCenter.addResponsesToPoll(pollID, [submittedResponse]);
77
+ updateSavedResponses(prev => {
78
+ const prevCopy = { ...prev };
79
+ prevCopy[index] = { option: singleOptionAnswer, options: Array.from(multipleOptionAnswer) };
80
+ return prevCopy;
81
+ });
76
82
  startTime.current = Date.now();
77
- }, [isValidVote, actions.interactivityCenter, pollID, index, singleOptionAnswer, multipleOptionAnswer]);
83
+ }, [
84
+ isValidVote,
85
+ index,
86
+ singleOptionAnswer,
87
+ multipleOptionAnswer,
88
+ actions.interactivityCenter,
89
+ pollID,
90
+ updateSavedResponses,
91
+ ]);
78
92
 
79
93
  return (
80
94
  <Box
@@ -147,7 +161,7 @@ export const QuestionCard = ({
147
161
  setAnswer={setSingleOptionAnswer}
148
162
  totalResponses={result?.totalResponses}
149
163
  showVoteCount={showVoteCount}
150
- localPeerResponse={localPeerResponse}
164
+ localPeerResponse={localPeerChoice}
151
165
  isStopped={pollState === 'stopped'}
152
166
  />
153
167
  ) : null}
@@ -163,27 +177,19 @@ export const QuestionCard = ({
163
177
  setSelectedOptions={setMultipleOptionAnswer}
164
178
  totalResponses={result?.totalResponses}
165
179
  showVoteCount={showVoteCount}
166
- localPeerResponse={localPeerResponse}
180
+ localPeerResponse={localPeerChoice}
167
181
  isStopped={pollState === 'stopped'}
168
182
  />
169
183
  ) : null}
170
184
  </Box>
171
185
  {isLive && (
172
- <QuestionActions
173
- isValidVote={isValidVote}
174
- onVote={handleVote}
175
- response={localPeerResponse}
176
- isQuiz={isQuiz}
177
- incrementIndex={() => {
178
- setCurrentIndex(curr => Math.min(totalQuestions, curr + 1));
179
- }}
180
- />
186
+ <QuestionActions isValidVote={isValidVote} onVote={handleVote} response={localPeerChoice} isQuiz={isQuiz} />
181
187
  )}
182
188
  </Box>
183
189
  );
184
190
  };
185
191
 
186
- const QuestionActions = ({ isValidVote, response, isQuiz, onVote, incrementIndex }) => {
192
+ const QuestionActions = ({ isValidVote, response, isQuiz, onVote }) => {
187
193
  return (
188
194
  <Flex align="center" justify="end" css={{ gap: '$4', w: '100%' }}>
189
195
  {response ? (
@@ -193,14 +199,7 @@ const QuestionActions = ({ isValidVote, response, isQuiz, onVote, incrementIndex
193
199
  {!isQuiz && !response.skipped ? 'Voted' : null}
194
200
  </Text>
195
201
  ) : (
196
- <Button
197
- css={{ p: '$xs $10', fontWeight: '$semiBold' }}
198
- disabled={!isValidVote}
199
- onClick={() => {
200
- onVote();
201
- incrementIndex();
202
- }}
203
- >
202
+ <Button css={{ p: '$xs $10', fontWeight: '$semiBold' }} disabled={!isValidVote} onClick={onVote}>
204
203
  {isQuiz ? 'Answer' : 'Vote'}
205
204
  </Button>
206
205
  )}
@@ -1,10 +1,18 @@
1
- import React from 'react';
1
+ import React, { Dispatch, SetStateAction } from 'react';
2
2
  import { HMSPoll } from '@100mslive/react-sdk';
3
3
  import { PeerParticipationSummary } from './PeerParticipationSummary';
4
4
  // @ts-ignore
5
5
  import { QuestionCard } from './QuestionCard';
6
6
 
7
- export const StandardView = ({ poll }: { poll: HMSPoll }) => {
7
+ export const StandardView = ({
8
+ poll,
9
+ localPeerResponses,
10
+ updateSavedResponses,
11
+ }: {
12
+ poll: HMSPoll;
13
+ localPeerResponses: Record<number, number | number[] | undefined>;
14
+ updateSavedResponses: Dispatch<SetStateAction<Record<any, any>>>;
15
+ }) => {
8
16
  if (!poll?.questions) {
9
17
  return null;
10
18
  }
@@ -28,11 +36,9 @@ export const StandardView = ({ poll }: { poll: HMSPoll }) => {
28
36
  result={question.result}
29
37
  totalQuestions={poll.questions?.length || 0}
30
38
  options={question.options}
31
- responses={question.responses}
39
+ localPeerResponse={localPeerResponses?.[question.index]}
32
40
  answer={question.answer}
33
- setCurrentIndex={() => {
34
- return;
35
- }}
41
+ updateSavedResponses={updateSavedResponses}
36
42
  rolesThatCanViewResponses={poll.rolesThatCanViewResponses}
37
43
  />
38
44
  ))}
@@ -1,16 +1,27 @@
1
- import React, { useState } from 'react';
2
- import { HMSPoll, selectLocalPeerID, useHMSStore } from '@100mslive/react-sdk';
1
+ import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
2
+ import { HMSPoll } from '@100mslive/react-sdk';
3
3
  // @ts-ignore
4
4
  import { QuestionCard } from './QuestionCard';
5
5
  // @ts-ignore
6
- import { getLastAttemptedIndex } from '../../../common/utils';
6
+ import { getIndexToShow } from '../../../common/utils';
7
7
 
8
- export const TimedView = ({ poll }: { poll: HMSPoll }) => {
9
- const localPeerId = useHMSStore(selectLocalPeerID);
10
- const lastAttemptedIndex = getLastAttemptedIndex(poll.questions, localPeerId, '');
11
- const [currentIndex, setCurrentIndex] = useState(lastAttemptedIndex);
8
+ export const TimedView = ({
9
+ poll,
10
+ localPeerResponses,
11
+ updateSavedResponses,
12
+ }: {
13
+ poll: HMSPoll;
14
+ localPeerResponses?: Record<number, number | number[] | undefined>;
15
+ updateSavedResponses: Dispatch<SetStateAction<Record<any, any>>>;
16
+ }) => {
17
+ const [currentIndex, setCurrentIndex] = useState(getIndexToShow(localPeerResponses));
12
18
  const activeQuestion = poll.questions?.find(question => question.index === currentIndex);
13
- const attemptedAll = poll.questions?.length === lastAttemptedIndex - 1;
19
+ const attemptedAll = (poll.questions?.length || 0) < currentIndex;
20
+
21
+ // Handles increments so only one question is shown at a time in quiz
22
+ useEffect(() => {
23
+ setCurrentIndex(getIndexToShow(localPeerResponses));
24
+ }, [localPeerResponses]);
14
25
 
15
26
  if ((!activeQuestion && !attemptedAll) || !poll.questions?.length) {
16
27
  return null;
@@ -32,10 +43,10 @@ export const TimedView = ({ poll }: { poll: HMSPoll }) => {
32
43
  result={question?.result}
33
44
  totalQuestions={poll.questions?.length || 0}
34
45
  options={question.options}
35
- responses={question.responses}
46
+ localPeerResponse={localPeerResponses?.[question.index]}
36
47
  answer={question.answer}
37
- setCurrentIndex={setCurrentIndex}
38
48
  rolesThatCanViewResponses={poll.rolesThatCanViewResponses}
49
+ updateSavedResponses={updateSavedResponses}
39
50
  />
40
51
  ) : null;
41
52
  })}
@@ -1,5 +1,6 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import {
3
+ selectLocalPeerID,
3
4
  selectPeerNameByID,
4
5
  selectPermissions,
5
6
  selectPollByID,
@@ -14,6 +15,8 @@ import { StandardView } from './StandardVoting';
14
15
  import { TimedView } from './TimedVoting';
15
16
  // @ts-ignore
16
17
  import { usePollViewState } from '../../AppData/useUISettings';
18
+ // @ts-ignore
19
+ import { getPeerResponses } from '../../../common/utils';
17
20
  import { StatusIndicator } from '../common/StatusIndicator';
18
21
  import { POLL_VIEWS } from '../../../common/constants';
19
22
 
@@ -26,6 +29,41 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
26
29
  const { setPollView } = usePollViewState();
27
30
  // Sets view - linear or vertical, toggles timer indicator
28
31
  const showSingleView = poll?.type === 'quiz' && poll.state === 'started';
32
+ const fetchedInitialResponses = useRef(false);
33
+ const [savedResponses, setSavedResponses] = useState<Record<any, any>>({});
34
+ const localPeerId = useHMSStore(selectLocalPeerID);
35
+
36
+ // To reset whenever a different poll is opened
37
+ useEffect(() => {
38
+ fetchedInitialResponses.current = false;
39
+ setSavedResponses({});
40
+ }, [id, setSavedResponses]);
41
+
42
+ useEffect(() => {
43
+ const getResponses = async () => {
44
+ if (poll && actions.interactivityCenter && !fetchedInitialResponses.current) {
45
+ await actions.interactivityCenter.getPollResponses(poll, true);
46
+ fetchedInitialResponses.current = true;
47
+ }
48
+ };
49
+ getResponses();
50
+ }, [poll, actions.interactivityCenter]);
51
+
52
+ useEffect(() => {
53
+ if (poll?.questions) {
54
+ const localPeerResponses = getPeerResponses(poll.questions, localPeerId);
55
+ // @ts-ignore
56
+ localPeerResponses?.forEach(response => {
57
+ if (response) {
58
+ setSavedResponses(prev => {
59
+ const prevCopy = { ...prev };
60
+ prevCopy[response[0]?.questionIndex] = { option: response[0]?.option, options: response[0]?.options };
61
+ return prevCopy;
62
+ });
63
+ }
64
+ });
65
+ }
66
+ }, [localPeerId, poll?.questions, id]);
29
67
 
30
68
  if (!poll) {
31
69
  return null;
@@ -74,7 +112,11 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
74
112
  </Text>
75
113
  ) : null}
76
114
 
77
- {showSingleView ? <TimedView poll={poll} /> : <StandardView poll={poll} />}
115
+ {showSingleView ? (
116
+ <TimedView poll={poll} localPeerResponses={savedResponses} updateSavedResponses={setSavedResponses} />
117
+ ) : (
118
+ <StandardView poll={poll} localPeerResponses={savedResponses} updateSavedResponses={setSavedResponses} />
119
+ )}
78
120
  </Flex>
79
121
  <Flex
80
122
  css={{ w: '100%', justifyContent: 'end', alignItems: 'center', p: '$8', borderTop: '1px solid $border_bright' }}
@@ -1,8 +1,9 @@
1
- import React, { useEffect, useMemo, useState } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
- import { selectLocalPeer, useHMSStore } from '@100mslive/react-sdk';
3
+ import { PeopleAddIcon } from '@100mslive/react-icons';
4
4
  import { Flex } from '../../../Layout';
5
5
  import { config as cssConfig } from '../../../Theme';
6
+ import { WaitingView } from '../../layouts/WaitingView';
6
7
  import { InsetTile } from '../InsetTile';
7
8
  import { Pagination } from '../Pagination';
8
9
  import { Grid } from './Grid';
@@ -13,26 +14,14 @@ import { usePagesWithTiles, useTileLayout } from '../hooks/useTileLayout';
13
14
  import { UI_SETTINGS } from '../../common/constants';
14
15
 
15
16
  export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, onPageSize, edgeToEdge }: LayoutProps) {
16
- const localPeer = useHMSStore(selectLocalPeer);
17
17
  const isMobile = useMedia(cssConfig.media.md);
18
18
  let maxTileCount = useUISettings(UI_SETTINGS.maxTileCount);
19
19
  maxTileCount = isMobile ? Math.min(maxTileCount, 6) : maxTileCount;
20
- let pageList = usePagesWithTiles({
20
+ const pageList = usePagesWithTiles({
21
21
  peers,
22
22
  maxTileCount,
23
23
  });
24
- // useMemo is needed to prevent recursion as new array is created for localPeer
25
- const inputPeers = useMemo(() => {
26
- if (pageList.length === 0) {
27
- return localPeer ? [localPeer] : [];
28
- }
29
- return peers;
30
- }, [pageList.length, peers, localPeer]);
31
- // Pass local peer to main grid if no other peer has tiles
32
- pageList = usePagesWithTiles({
33
- peers: inputPeers,
34
- maxTileCount,
35
- });
24
+
36
25
  const { ref, pagesWithTiles } = useTileLayout({
37
26
  pageList,
38
27
  maxTileCount,
@@ -60,7 +49,14 @@ export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, o
60
49
  numPages={pagesWithTiles.length}
61
50
  />
62
51
  )}
63
- {isInsetEnabled && pageList.length > 0 && pageList[0][0].peer.id !== localPeer?.id && <InsetTile />}
52
+ {pageList.length === 0 ? (
53
+ <WaitingView
54
+ title="You're the first to join"
55
+ subtitle="Sit back and relax till others join"
56
+ icon={<PeopleAddIcon width="56px" height="56px" style={{ color: 'white' }} />}
57
+ />
58
+ ) : null}
59
+ {isInsetEnabled && <InsetTile />}
64
60
  </Flex>
65
61
  );
66
62
  }