@100mslive/roomkit-react 0.1.14 → 0.1.16

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 (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,