@100mslive/roomkit-react 0.1.14 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. package/dist/{HLSView-662T7R7H.js → HLSView-EMUOLCTM.js} +128 -39
  2. package/dist/HLSView-EMUOLCTM.js.map +7 -0
  3. package/dist/Prebuilt/common/PeersSorter.d.ts +1 -0
  4. package/dist/Prebuilt/common/constants.d.ts +9 -5
  5. package/dist/Prebuilt/common/hooks.d.ts +1 -0
  6. package/dist/Prebuilt/components/Footer/ParticipantList.d.ts +17 -0
  7. package/dist/Prebuilt/components/Footer/RoleAccordion.d.ts +3 -2
  8. package/dist/Prebuilt/components/Footer/WhiteboardToggle.d.ts +2 -0
  9. package/dist/Prebuilt/components/HMSVideo/HLSCaptionSelector.d.ts +5 -0
  10. package/dist/Prebuilt/components/Notifications/HandRaisedNotifications.d.ts +1 -0
  11. package/dist/Prebuilt/components/Polls/Voting/Leaderboard.d.ts +4 -0
  12. package/dist/Prebuilt/components/Polls/Voting/LeaderboardEntry.d.ts +9 -0
  13. package/dist/Prebuilt/components/Polls/Voting/PeerParticipationSummary.d.ts +5 -0
  14. package/dist/Prebuilt/components/PreviousRoleInMetadata.d.ts +1 -0
  15. package/dist/Prebuilt/components/RemoveParticipant.d.ts +5 -0
  16. package/dist/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.d.ts +4 -0
  17. package/dist/Prebuilt/layouts/WhiteboardView.d.ts +2 -0
  18. package/dist/{chunk-2B7YYNHQ.js → chunk-ZYR4B4KQ.js} +2240 -1767
  19. package/dist/chunk-ZYR4B4KQ.js.map +7 -0
  20. package/dist/index.cjs.js +2805 -2172
  21. package/dist/index.cjs.js.map +4 -4
  22. package/dist/index.js +1 -1
  23. package/dist/meta.cjs.json +739 -177
  24. package/dist/meta.esbuild.json +749 -186
  25. package/package.json +7 -7
  26. package/src/Prebuilt/AppStateContext.tsx +1 -1
  27. package/src/Prebuilt/common/PeersSorter.ts +24 -8
  28. package/src/Prebuilt/common/constants.ts +6 -6
  29. package/src/Prebuilt/common/hooks.ts +16 -0
  30. package/src/Prebuilt/common/utils.js +33 -0
  31. package/src/Prebuilt/components/AppData/AppData.tsx +1 -16
  32. package/src/Prebuilt/components/Chat/Chat.jsx +10 -34
  33. package/src/Prebuilt/components/Chat/ChatBody.jsx +107 -66
  34. package/src/Prebuilt/components/Chat/ChatFooter.tsx +21 -12
  35. package/src/Prebuilt/components/Chat/ChatSelector.tsx +25 -25
  36. package/src/Prebuilt/components/Chat/ChatSelectorContainer.tsx +15 -16
  37. package/src/Prebuilt/components/Chat/PinnedMessage.tsx +7 -2
  38. package/src/Prebuilt/components/ConferenceScreen.tsx +2 -0
  39. package/src/Prebuilt/components/Footer/ChatToggle.tsx +30 -7
  40. package/src/Prebuilt/components/Footer/Footer.tsx +2 -1
  41. package/src/Prebuilt/components/Footer/PaginatedParticipants.tsx +0 -1
  42. package/src/Prebuilt/components/Footer/{ParticipantList.jsx → ParticipantList.tsx} +169 -127
  43. package/src/Prebuilt/components/Footer/RoleAccordion.tsx +23 -13
  44. package/src/Prebuilt/components/Footer/WhiteboardToggle.tsx +34 -0
  45. package/src/Prebuilt/components/HMSVideo/HLSCaptionSelector.tsx +13 -0
  46. package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +34 -2
  47. package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +35 -0
  48. package/src/Prebuilt/components/Notifications/Notifications.tsx +47 -14
  49. package/src/Prebuilt/components/Notifications/PeerNotifications.tsx +7 -2
  50. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +3 -9
  51. package/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx +21 -1
  52. package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.jsx +34 -7
  53. package/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.jsx +2 -2
  54. package/src/Prebuilt/components/Polls/Polls.tsx +3 -0
  55. package/src/Prebuilt/components/Polls/Voting/Leaderboard.tsx +115 -0
  56. package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +63 -0
  57. package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +38 -0
  58. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +33 -8
  59. package/src/Prebuilt/components/Polls/Voting/StandardVoting.jsx +7 -1
  60. package/src/Prebuilt/components/Polls/Voting/Voting.jsx +31 -13
  61. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +33 -21
  62. package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +47 -35
  63. package/src/Prebuilt/components/Polls/common/StatusIndicator.jsx +2 -22
  64. package/src/Prebuilt/components/Polls/common/VoteCount.jsx +1 -15
  65. package/src/Prebuilt/components/PreviousRoleInMetadata.tsx +21 -0
  66. package/src/Prebuilt/components/RemoveParticipant.tsx +35 -0
  67. package/src/Prebuilt/components/RoleChangeModal.jsx +1 -1
  68. package/src/Prebuilt/components/SidePaneTabs.tsx +0 -1
  69. package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +1 -1
  70. package/src/Prebuilt/components/Toast/ToastConfig.jsx +15 -3
  71. package/src/Prebuilt/components/VideoLayouts/EqualProminence.tsx +6 -5
  72. package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +27 -5
  73. package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +0 -1
  74. package/src/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.tsx +24 -0
  75. package/src/Prebuilt/layouts/HLSView.jsx +51 -3
  76. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +20 -3
  77. package/src/Prebuilt/layouts/WhiteboardView.tsx +66 -0
  78. package/dist/HLSView-662T7R7H.js.map +0 -7
  79. package/dist/chunk-2B7YYNHQ.js.map +0 -7
  80. package/src/Prebuilt/components/AppData/useAppLayout.js +0 -6
  81. package/src/Prebuilt/components/init/initUtils.js +0 -67
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
  import React, { useCallback, useMemo, useState } from 'react';
3
3
  import { selectLocalPeer, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
4
- import { ChevronLeftIcon, ChevronRightIcon } from '@100mslive/react-icons';
4
+ import { CheckCircleIcon, ChevronLeftIcon, ChevronRightIcon, CrossCircleIcon } from '@100mslive/react-icons';
5
5
  import { Box, Button, Flex, IconButton, Input, styled, Text } from '../../../../';
6
6
  import { checkCorrectAnswer } from '../../../common/utils';
7
7
  import { MultipleChoiceOptions } from '../common/MultipleChoiceOptions';
@@ -48,7 +48,8 @@ export const QuestionCard = ({
48
48
  !rolesThatCanViewResponses ||
49
49
  rolesThatCanViewResponses.length === 0 ||
50
50
  rolesThatCanViewResponses.includes(localPeerRoleName || '');
51
- const showVoteCount = roleCanViewResponse && (localPeerResponse || (isLocalPeerCreator && pollState === 'stopped'));
51
+ const showVoteCount =
52
+ roleCanViewResponse && (localPeerResponse || (isLocalPeerCreator && pollState === 'stopped')) && !isQuiz;
52
53
 
53
54
  const isLive = pollState === 'started';
54
55
  const canRespond = isLive && !localPeerResponse;
@@ -72,6 +73,8 @@ export const QuestionCard = ({
72
73
 
73
74
  const stringAnswerExpected = [QUESTION_TYPE.LONG_ANSWER, QUESTION_TYPE.SHORT_ANSWER].includes(type);
74
75
 
76
+ const respondedToQuiz = isQuiz && localPeerResponse && !localPeerResponse.skipped;
77
+
75
78
  const isValidVote = useMemo(() => {
76
79
  if (stringAnswerExpected) {
77
80
  return textAnswer.length > 0;
@@ -114,13 +117,29 @@ export const QuestionCard = ({
114
117
  p: '$md',
115
118
  mt: '$md',
116
119
  border:
117
- isQuiz && localPeerResponse && !localPeerResponse.skipped
120
+ respondedToQuiz && !isLive
118
121
  ? `1px solid ${isCorrectAnswer ? '$alert_success' : '$alert_error_default'}`
119
122
  : 'none',
120
123
  }}
121
124
  >
122
125
  <Flex align="center" justify="between">
123
- <Text variant="caption" css={{ color: '$on_surface_low', fontWeight: '$semiBold' }}>
126
+ <Text
127
+ variant="caption"
128
+ css={{
129
+ color:
130
+ respondedToQuiz && !isLive
131
+ ? isCorrectAnswer
132
+ ? '$alert_success'
133
+ : '$alert_error_default'
134
+ : '$on_surface_low',
135
+ fontWeight: '$semiBold',
136
+ display: 'flex',
137
+ alignItems: 'center',
138
+ gap: '$4',
139
+ }}
140
+ >
141
+ {respondedToQuiz && isCorrectAnswer ? <CheckCircleIcon height={20} width={20} /> : null}
142
+ {respondedToQuiz && !isCorrectAnswer ? <CrossCircleIcon height={20} width={20} /> : null}
124
143
  QUESTION {index} OF {totalQuestions}: {type.toUpperCase()}
125
144
  </Text>
126
145
 
@@ -196,6 +215,8 @@ export const QuestionCard = ({
196
215
  setAnswer={setSingleOptionAnswer}
197
216
  totalResponses={result?.totalResponses}
198
217
  showVoteCount={showVoteCount}
218
+ localPeerResponse={localPeerResponse}
219
+ isStopped={pollState === 'stopped'}
199
220
  />
200
221
  ) : null}
201
222
 
@@ -211,6 +232,8 @@ export const QuestionCard = ({
211
232
  setSelectedOptions={setMultipleOptionAnswer}
212
233
  totalResponses={result?.totalResponses}
213
234
  showVoteCount={showVoteCount}
235
+ localPeerResponse={localPeerResponse}
236
+ isStopped={pollState === 'stopped'}
214
237
  />
215
238
  ) : null}
216
239
 
@@ -221,14 +244,14 @@ export const QuestionCard = ({
221
244
  onSkip={handleSkip}
222
245
  onVote={handleVote}
223
246
  response={localPeerResponse}
224
- stringAnswerExpected={stringAnswerExpected}
247
+ isQuiz={isQuiz}
225
248
  />
226
249
  )}
227
250
  </Box>
228
251
  );
229
252
  };
230
253
 
231
- const QuestionActions = ({ isValidVote, skippable, response, stringAnswerExpected, onVote, onSkip }) => {
254
+ const QuestionActions = ({ isValidVote, skippable, response, isQuiz, onVote, onSkip }) => {
232
255
  return (
233
256
  <Flex align="center" justify="end" css={{ gap: '$4', w: '100%' }}>
234
257
  {skippable && !response ? (
@@ -239,11 +262,13 @@ const QuestionActions = ({ isValidVote, skippable, response, stringAnswerExpecte
239
262
 
240
263
  {response ? (
241
264
  <Text css={{ fontWeight: '$semiBold', color: '$on_surface_medium' }}>
242
- {response.skipped ? 'Skipped' : stringAnswerExpected ? 'Submitted' : 'Voted'}
265
+ {response.skipped ? 'Skipped' : null}
266
+ {isQuiz && !response.skipped ? 'Answered' : null}
267
+ {!isQuiz && !response.skipped ? 'Voted' : null}
243
268
  </Text>
244
269
  ) : (
245
270
  <Button css={{ p: '$xs $10', fontWeight: '$semiBold' }} disabled={!isValidVote} onClick={onVote}>
246
- {stringAnswerExpected ? 'Submit' : 'Vote'}
271
+ {isQuiz ? 'Answer' : 'Vote'}
247
272
  </Button>
248
273
  )}
249
274
  </Flex>
@@ -1,5 +1,6 @@
1
1
  // @ts-check
2
2
  import React from 'react';
3
+ import { PeerParticipationSummary } from './PeerParticipationSummary';
3
4
  import { QuestionCard } from './QuestionCard';
4
5
 
5
6
  /**
@@ -11,12 +12,17 @@ export const StandardView = ({ poll }) => {
11
12
  if (!poll?.questions) {
12
13
  return null;
13
14
  }
15
+
16
+ const isQuiz = poll.type === 'quiz';
17
+ const isStopped = poll.state === 'stopped';
18
+
14
19
  return (
15
20
  <>
21
+ {isQuiz && isStopped ? <PeerParticipationSummary poll={poll} /> : null}
16
22
  {poll.questions?.map((question, index) => (
17
23
  <QuestionCard
18
24
  pollID={poll.id}
19
- isQuiz={poll.type === 'quiz'}
25
+ isQuiz={isQuiz}
20
26
  startedBy={poll.startedBy}
21
27
  pollState={poll.state}
22
28
  key={`${question.text}-${index}`}
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  import {
4
4
  selectLocalPeerID,
5
5
  selectPeerNameByID,
6
+ selectPermissions,
6
7
  selectPollByID,
7
8
  useHMSActions,
8
9
  useHMSStore,
@@ -23,11 +24,18 @@ export const Voting = ({ id, toggleVoting }) => {
23
24
  const pollCreatorName = useHMSStore(selectPeerNameByID(poll?.createdBy));
24
25
  const isLocalPeerCreator = useHMSStore(selectLocalPeerID) === poll?.createdBy;
25
26
  const { setPollView } = usePollViewState();
27
+ const permissions = useHMSStore(selectPermissions);
28
+
29
+ // const sharedLeaderboards = useHMSStore(selectSessionStore(SESSION_STORE_KEY.SHARED_LEADERBOARDS));
26
30
 
27
31
  if (!poll) {
28
32
  return null;
29
33
  }
30
34
 
35
+ // const isLeaderboardShared = (sharedLeaderboards || []).includes(id);
36
+ const canViewLeaderboard =
37
+ poll.type === 'quiz' && poll.state === 'stopped' && !poll.anonymous && permissions?.pollWrite;
38
+
31
39
  // Sets view - linear or vertical, toggles timer indicator
32
40
  const isTimed = (poll.duration || 0) > 0;
33
41
  const isLive = poll.state === 'started';
@@ -52,8 +60,8 @@ export const Voting = ({ id, toggleVoting }) => {
52
60
  >
53
61
  <ChevronLeftIcon />
54
62
  </Flex>
55
- <Text variant="h6">{poll?.type?.toUpperCase()}</Text>
56
- <StatusIndicator isLive={isLive} shouldShowTimer={isLive && isTimed} />
63
+ <Text variant="h6">{poll.title}</Text>
64
+ <StatusIndicator isLive={isLive} />
57
65
  <Box
58
66
  css={{
59
67
  marginLeft: 'auto',
@@ -72,18 +80,8 @@ export const Voting = ({ id, toggleVoting }) => {
72
80
  {pollCreatorName || 'Participant'} started a {poll.type}
73
81
  </Text>
74
82
  </Box>
75
- {poll.state === 'started' && isLocalPeerCreator && (
76
- <Box css={{ flex: 'initial' }}>
77
- <Button
78
- variant="danger"
79
- css={{ fontSize: '$sm', fontWeight: '$semiBold', p: '$3 $6' }}
80
- onClick={() => actions.interactivityCenter.stopPoll(id)}
81
- >
82
- End {poll.type}
83
- </Button>
84
- </Box>
85
- )}
86
83
  </Flex>
84
+
87
85
  {/* {poll.state === "stopped" && (
88
86
  <PollResultSummary
89
87
  pollResult={poll.result}
@@ -92,7 +90,27 @@ export const Voting = ({ id, toggleVoting }) => {
92
90
  isAdmin={isLocalPeerCreator}
93
91
  />
94
92
  )} */}
93
+
95
94
  {isTimed ? <TimedView poll={poll} /> : <StandardView poll={poll} />}
95
+
96
+ {poll.state === 'started' && isLocalPeerCreator && (
97
+ <Button
98
+ variant="danger"
99
+ css={{ fontWeight: '$semiBold', w: 'max-content', ml: 'auto', mt: '$8' }}
100
+ onClick={() => actions.interactivityCenter.stopPoll(id)}
101
+ >
102
+ End {poll.type}
103
+ </Button>
104
+ )}
105
+
106
+ {canViewLeaderboard ? (
107
+ <Button
108
+ css={{ fontWeight: '$semiBold', w: 'max-content', ml: 'auto', mt: '$8' }}
109
+ onClick={() => setPollView(POLL_VIEWS.RESULTS)}
110
+ >
111
+ View Leaderboard
112
+ </Button>
113
+ ) : null}
96
114
  </Flex>
97
115
  </Container>
98
116
  );
@@ -1,6 +1,6 @@
1
1
  // @ts-check
2
2
  import React from 'react';
3
- import { CheckIcon } from '@100mslive/react-icons';
3
+ import { CheckCircleIcon, CheckIcon } from '@100mslive/react-icons';
4
4
  import { Checkbox, Flex, Label, Text } from '../../../../';
5
5
  import { OptionInputWithDelete } from './OptionInputWithDelete';
6
6
  import { VoteCount } from './VoteCount';
@@ -8,15 +8,17 @@ import { VoteProgress } from './VoteProgress';
8
8
 
9
9
  export const MultipleChoiceOptions = ({
10
10
  questionIndex,
11
- isQuiz,
12
11
  options,
13
- correctOptionIndexes,
14
12
  canRespond,
15
13
  response,
16
14
  totalResponses,
17
15
  selectedOptions,
18
16
  setSelectedOptions,
19
17
  showVoteCount,
18
+ isQuiz,
19
+ correctOptionIndexes,
20
+ localPeerResponse,
21
+ isStopped,
20
22
  }) => {
21
23
  const handleCheckedChange = (checked, index) => {
22
24
  const newSelected = new Set(selectedOptions);
@@ -31,35 +33,45 @@ export const MultipleChoiceOptions = ({
31
33
  return (
32
34
  <Flex direction="column" css={{ gap: '$md', w: '100%', mb: '$md' }}>
33
35
  {options.map(option => {
34
- const isCorrectAnswer = isQuiz && correctOptionIndexes?.includes(option.index);
35
-
36
36
  return (
37
37
  <Flex align="center" key={`${questionIndex}-${option.index}`} css={{ w: '100%', gap: '$9' }}>
38
- <Checkbox.Root
39
- id={`${questionIndex}-${option.index}`}
40
- disabled={!canRespond}
41
- checked={response?.options?.includes(option.index)}
42
- onCheckedChange={checked => handleCheckedChange(checked, option.index)}
43
- css={{
44
- cursor: canRespond ? 'pointer' : 'not-allowed',
45
- }}
46
- >
47
- <Checkbox.Indicator>
48
- <CheckIcon width={16} height={16} />
49
- </Checkbox.Indicator>
50
- </Checkbox.Root>
38
+ {!isStopped || !isQuiz ? (
39
+ <Checkbox.Root
40
+ id={`${questionIndex}-${option.index}`}
41
+ disabled={!canRespond}
42
+ checked={response?.options?.includes(option.index)}
43
+ onCheckedChange={checked => handleCheckedChange(checked, option.index)}
44
+ css={{
45
+ cursor: canRespond ? 'pointer' : 'not-allowed',
46
+ }}
47
+ >
48
+ <Checkbox.Indicator>
49
+ <CheckIcon width={16} height={16} />
50
+ </Checkbox.Indicator>
51
+ </Checkbox.Root>
52
+ ) : null}
53
+
54
+ {isStopped && correctOptionIndexes.includes(option.index) ? (
55
+ <Flex css={{ color: '$on_surface_high' }}>
56
+ <CheckCircleIcon />
57
+ </Flex>
58
+ ) : null}
51
59
 
52
60
  <Flex direction="column" css={{ flexGrow: '1' }}>
53
61
  <Flex css={{ w: '100%' }}>
54
62
  <Text css={{ display: 'flex', flexGrow: '1' }}>
55
63
  <Label htmlFor={`${questionIndex}-${option.index}`}>{option.text}</Label>
56
64
  </Text>
57
- {showVoteCount && (
58
- <VoteCount isQuiz={isQuiz} isCorrectAnswer={isCorrectAnswer} voteCount={option.voteCount} />
59
- )}
65
+ {showVoteCount && <VoteCount voteCount={option.voteCount} />}
60
66
  </Flex>
61
67
  {showVoteCount && <VoteProgress option={option} totalResponses={totalResponses} />}
62
68
  </Flex>
69
+
70
+ {isStopped && isQuiz && localPeerResponse?.options.includes(option.index) ? (
71
+ <Text variant="sm" css={{ color: '$on_surface_medium', maxWidth: 'max-content' }}>
72
+ Your Answer
73
+ </Text>
74
+ ) : null}
63
75
  </Flex>
64
76
  );
65
77
  })}
@@ -1,5 +1,6 @@
1
1
  // @ts-check
2
2
  import React from 'react';
3
+ import { CheckCircleIcon } from '@100mslive/react-icons';
3
4
  import { Flex, Label, RadioGroup, Text } from '../../../../';
4
5
  import { OptionInputWithDelete } from './OptionInputWithDelete';
5
6
  import { VoteCount } from './VoteCount';
@@ -7,64 +8,75 @@ import { VoteProgress } from './VoteProgress';
7
8
 
8
9
  export const SingleChoiceOptions = ({
9
10
  questionIndex,
10
- isQuiz,
11
11
  options,
12
12
  response,
13
13
  canRespond,
14
- correctOptionIndex,
15
14
  setAnswer,
16
15
  totalResponses,
17
16
  showVoteCount,
17
+ correctOptionIndex,
18
+ isStopped,
19
+ isQuiz,
20
+ localPeerResponse,
18
21
  }) => {
19
22
  return (
20
23
  <RadioGroup.Root value={response?.option} onValueChange={value => setAnswer(value)}>
21
24
  <Flex direction="column" css={{ gap: '$md', w: '100%', mb: '$md' }}>
22
25
  {options.map(option => {
23
- const isCorrectAnswer = isQuiz && option.index === correctOptionIndex;
24
-
25
26
  return (
26
- <Flex align="center" key={`${questionIndex}-${option.index}`} css={{ w: '100%', gap: '$5' }}>
27
- <RadioGroup.Item
28
- css={{
29
- background: 'none',
30
- h: '$9',
31
- w: '$9',
32
- border: '2px solid',
33
- borderColor: '$on_surface_high',
34
- display: 'flex',
35
- justifyContent: 'center',
36
- alignItems: 'center',
37
- cursor: canRespond ? 'pointer' : 'not-allowed',
38
- '&[data-state="checked"]': {
39
- borderColor: '$primary_bright',
40
- borderWidth: '2px',
41
- },
42
- }}
43
- disabled={!canRespond}
44
- value={option.index}
45
- id={`${questionIndex}-${option.index}`}
46
- >
47
- <RadioGroup.Indicator
27
+ <Flex align="start" key={`${questionIndex}-${option.index}`} css={{ w: '100%', gap: '$5' }}>
28
+ {!isStopped || !isQuiz ? (
29
+ <RadioGroup.Item
48
30
  css={{
49
- h: '80%',
50
- w: '80%',
51
- background: '$primary_bright',
52
- borderRadius: '$round',
31
+ background: 'none',
32
+ h: '$9',
33
+ w: '$9',
34
+ border: '2px solid',
35
+ borderColor: '$on_surface_high',
36
+ display: 'flex',
37
+ justifyContent: 'center',
38
+ alignItems: 'center',
39
+ cursor: canRespond ? 'pointer' : 'not-allowed',
40
+ '&[data-state="checked"]': {
41
+ borderColor: '$primary_bright',
42
+ borderWidth: '2px',
43
+ },
53
44
  }}
54
- />
55
- </RadioGroup.Item>
45
+ disabled={!canRespond}
46
+ value={option.index}
47
+ id={`${questionIndex}-${option.index}`}
48
+ >
49
+ <RadioGroup.Indicator
50
+ css={{
51
+ h: '80%',
52
+ w: '80%',
53
+ background: '$primary_bright',
54
+ borderRadius: '$round',
55
+ }}
56
+ />
57
+ </RadioGroup.Item>
58
+ ) : null}
59
+
60
+ {isStopped && correctOptionIndex === option.index && isQuiz ? (
61
+ <Flex css={{ color: '$on_surface_high' }}>
62
+ <CheckCircleIcon />
63
+ </Flex>
64
+ ) : null}
56
65
 
57
66
  <Flex direction="column" css={{ flexGrow: '1' }}>
58
67
  <Flex css={{ w: '100%' }}>
59
68
  <Text css={{ display: 'flex', flexGrow: '1' }}>
60
69
  <Label htmlFor={`${questionIndex}-${option.index}`}>{option.text}</Label>
61
70
  </Text>
62
- {showVoteCount && (
63
- <VoteCount isQuiz={isQuiz} isCorrectAnswer={isCorrectAnswer} voteCount={option.voteCount} />
64
- )}
71
+ {showVoteCount && <VoteCount voteCount={option.voteCount} />}
65
72
  </Flex>
66
73
  {showVoteCount && <VoteProgress option={option} totalResponses={totalResponses} />}
67
74
  </Flex>
75
+ {isStopped && isQuiz && localPeerResponse?.option === option.index ? (
76
+ <Text variant="sm" css={{ color: '$on_surface_medium', maxWidth: 'max-content' }}>
77
+ Your Answer
78
+ </Text>
79
+ ) : null}
68
80
  </Flex>
69
81
  );
70
82
  })}
@@ -2,14 +2,14 @@
2
2
  import React from 'react';
3
3
  import { Flex, Text } from '../../../../';
4
4
 
5
- export const StatusIndicator = ({ isLive, shouldShowTimer }) => {
5
+ export const StatusIndicator = ({ isLive }) => {
6
6
  return (
7
7
  <Flex align="center">
8
8
  <Flex
9
9
  css={{
10
10
  backgroundColor: isLive ? '$alert_error_default' : '$secondary_default',
11
11
  p: '$2 $4',
12
- borderRadius: shouldShowTimer ? '$0 0 0 $0' : '$0',
12
+ borderRadius: '$0',
13
13
  }}
14
14
  >
15
15
  <Text
@@ -22,26 +22,6 @@ export const StatusIndicator = ({ isLive, shouldShowTimer }) => {
22
22
  {isLive ? 'LIVE' : 'ENDED'}
23
23
  </Text>
24
24
  </Flex>
25
-
26
- {shouldShowTimer ? (
27
- <Flex
28
- css={{
29
- borderRadius: '0 $0 $0 0',
30
- p: '$2 $4',
31
- backgroundColor: '$background_default',
32
- }}
33
- >
34
- <Text
35
- variant="caption"
36
- css={{
37
- fontWeight: '$semiBold',
38
- color: '$on_surface_high',
39
- }}
40
- >
41
- 0:32
42
- </Text>
43
- </Flex>
44
- ) : null}
45
25
  </Flex>
46
26
  );
47
27
  };
@@ -2,23 +2,9 @@
2
2
  import React from 'react';
3
3
  import { Flex, Text } from '../../../../';
4
4
 
5
- export const VoteCount = ({ isQuiz, voteCount, isCorrectAnswer }) => {
5
+ export const VoteCount = ({ voteCount }) => {
6
6
  return (
7
7
  <Flex css={{ alignItems: 'center' }}>
8
- {isQuiz && (
9
- <Text
10
- variant="xs"
11
- css={{
12
- p: '$2',
13
- mr: '$2',
14
- color: isCorrectAnswer ? '$alert_success' : '$alert_error_default',
15
- borderRadius: '$1',
16
- border: `1px solid ${isCorrectAnswer ? '$alert_success' : '$alert_error_default'}`,
17
- }}
18
- >
19
- {isCorrectAnswer ? 'Correct' : 'Incorrect'}
20
- </Text>
21
- )}
22
8
  {voteCount ? (
23
9
  <Text variant="sm" css={{ color: '$on_surface_medium' }}>
24
10
  {voteCount}&nbsp;
@@ -0,0 +1,21 @@
1
+ import { useEffect } from 'react';
2
+ import { selectLocalPeerRoleName, useHMSVanillaStore } from '@100mslive/react-sdk';
3
+ // @ts-ignore: No implicit Any
4
+ import { useMyMetadata } from './hooks/useMetadata';
5
+
6
+ export const PreviousRoleInMetadata = () => {
7
+ const vanillaStore = useHMSVanillaStore();
8
+ const { updateMetaData } = useMyMetadata();
9
+
10
+ useEffect(() => {
11
+ let previousRole = vanillaStore.getState(selectLocalPeerRoleName);
12
+ const unsubscribe = vanillaStore.subscribe(currentRole => {
13
+ if (previousRole !== currentRole) {
14
+ updateMetaData({ prevRole: previousRole });
15
+ previousRole = currentRole;
16
+ }
17
+ }, selectLocalPeerRoleName);
18
+ return unsubscribe;
19
+ }, [vanillaStore]); //eslint-disable-line
20
+ return null;
21
+ };
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import { HMSPeerID, selectLocalPeerID, selectPermissions, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
3
+ import { PeopleRemoveIcon } from '@100mslive/react-icons';
4
+ // @ts-ignore: No implicit Any
5
+ import { ToastManager } from './Toast/ToastManager';
6
+ import { Dropdown } from '../../Dropdown';
7
+ import { Text } from '../../Text';
8
+
9
+ export const RemoveParticipant = ({ peerId }: { peerId: HMSPeerID }) => {
10
+ const canRemoveOthers = useHMSStore(selectPermissions)?.removeOthers;
11
+ const localPeerId = useHMSStore(selectLocalPeerID);
12
+ const actions = useHMSActions();
13
+
14
+ if (peerId === localPeerId || !canRemoveOthers) {
15
+ return null;
16
+ }
17
+ return (
18
+ <Dropdown.Item
19
+ css={{ color: '$alert_error_default', bg: '$surface_default' }}
20
+ onClick={async () => {
21
+ try {
22
+ await actions.removePeer(peerId, '');
23
+ } catch (error) {
24
+ const ex = error as Error;
25
+ ToastManager.addToast({ title: ex.message, variant: 'error' });
26
+ }
27
+ }}
28
+ >
29
+ <PeopleRemoveIcon />
30
+ <Text variant="sm" css={{ ml: '$4', color: 'inherit', fontWeight: '$semiBold' }}>
31
+ Remove Participant
32
+ </Text>
33
+ </Dropdown.Item>
34
+ );
35
+ };
@@ -170,7 +170,7 @@ export const RoleChangeModal = ({ peerId, onOpenChange }) => {
170
170
  variant="primary"
171
171
  css={{ width: '100%' }}
172
172
  onClick={async () => {
173
- await hmsActions.changeRole(peerId, selectedRole, peer.isLocal ? true : !requestPermission);
173
+ await hmsActions.changeRoleOfPeer(peerId, selectedRole, peer.isLocal ? true : !requestPermission);
174
174
  onOpenChange(false);
175
175
  }}
176
176
  >
@@ -6,7 +6,6 @@ import { CrossIcon } from '@100mslive/react-icons';
6
6
  // @ts-ignore: No implicit Any
7
7
  import { Chat } from './Chat/Chat';
8
8
  import { PaginatedParticipants } from './Footer/PaginatedParticipants';
9
- // @ts-ignore: No implicit Any
10
9
  import { ParticipantList } from './Footer/ParticipantList';
11
10
  import { Box, config as cssConfig, Flex, IconButton, Tabs, Text } from '../..';
12
11
  import { Tooltip } from '../../Tooltip';
@@ -287,7 +287,7 @@ export const TileMenuContent = ({
287
287
  toggleAudio();
288
288
  closeSheetOnClick();
289
289
  }}
290
- data-testid={isVideoEnabled ? 'mute_audio_participant_btn' : 'unmute_audio_participant_btn'}
290
+ data-testid={isAudioEnabled ? 'mute_audio_participant_btn' : 'unmute_audio_participant_btn'}
291
291
  >
292
292
  {isAudioEnabled ? <MicOnIcon height={20} width={20} /> : <MicOffIcon height={20} width={20} />}
293
293
  <span>{isAudioEnabled ? 'Mute' : 'Request Unmute'}</span>
@@ -38,15 +38,27 @@ const HandRaiseAction = React.forwardRef(({ id = '', isSingleHandRaise = true },
38
38
  bring_to_stage_label,
39
39
  on_stage_role,
40
40
  off_stage_roles = [],
41
+ skip_preview_for_role_change = false,
41
42
  } = layout?.screens?.conferencing?.default?.elements.on_stage_exp || {};
42
43
 
43
- const onClickHandler = useCallback(() => {
44
+ const onClickHandler = useCallback(async () => {
44
45
  if (isSingleHandRaise) {
45
- hmsActions.changeRoleOfPeer(id, on_stage_role);
46
+ hmsActions.changeRoleOfPeer(id, on_stage_role, skip_preview_for_role_change);
47
+ if (skip_preview_for_role_change) {
48
+ await hmsActions.lowerRemotePeerHand(id);
49
+ }
46
50
  } else {
47
51
  !isParticipantsOpen && toggleSidepane();
48
52
  }
49
- }, [hmsActions, id, isParticipantsOpen, isSingleHandRaise, on_stage_role, toggleSidepane]);
53
+ }, [
54
+ hmsActions,
55
+ id,
56
+ isParticipantsOpen,
57
+ isSingleHandRaise,
58
+ on_stage_role,
59
+ toggleSidepane,
60
+ skip_preview_for_role_change,
61
+ ]);
50
62
 
51
63
  // show nothing if handRaise is single and peer role is not hls
52
64
  if (isSingleHandRaise && (!peer || !off_stage_roles.includes(peer.roleName))) {
@@ -10,7 +10,6 @@ import { LayoutProps } from './interface';
10
10
  // @ts-ignore: No implicit Any
11
11
  import { useUISettings } from '../AppData/useUISettings';
12
12
  import { usePagesWithTiles, useTileLayout } from '../hooks/useTileLayout';
13
- // @ts-ignore: No implicit Any
14
13
  import { UI_SETTINGS } from '../../common/constants';
15
14
 
16
15
  export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, onPageSize, edgeToEdge }: LayoutProps) {
@@ -23,10 +22,12 @@ export function EqualProminence({ isInsetEnabled = false, peers, onPageChange, o
23
22
  maxTileCount,
24
23
  });
25
24
  // useMemo is needed to prevent recursion as new array is created for localPeer
26
- const inputPeers = useMemo(
27
- () => (pageList.length === 0 ? (localPeer ? [localPeer] : []) : peers),
28
- [pageList.length, peers, localPeer],
29
- );
25
+ const inputPeers = useMemo(() => {
26
+ if (pageList.length === 0) {
27
+ return localPeer ? [localPeer] : [];
28
+ }
29
+ return peers;
30
+ }, [pageList.length, peers, localPeer]);
30
31
  // Pass local peer to main grid if no other peer has tiles
31
32
  pageList = usePagesWithTiles({
32
33
  peers: inputPeers,