@100mslive/roomkit-react 0.3.10 → 0.3.11-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 (53) hide show
  1. package/dist/{HLSView-PF44ZBXN.js → HLSView-HJ44JWJK.js} +18 -3
  2. package/dist/HLSView-HJ44JWJK.js.map +7 -0
  3. package/dist/{HLSView-6J5FD3IV.css → HLSView-IBWU4R7W.css} +3 -3
  4. package/dist/{HLSView-6J5FD3IV.css.map → HLSView-IBWU4R7W.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-WDZ4KRYM.js} +2095 -1805
  13. package/dist/chunk-WDZ4KRYM.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="Waiting for Host 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
  }