@100mslive/roomkit-react 0.1.14 → 0.1.15

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 (43) hide show
  1. package/dist/{HLSView-662T7R7H.js → HLSView-MXBOUQBG.js} +128 -39
  2. package/dist/HLSView-MXBOUQBG.js.map +7 -0
  3. package/dist/Prebuilt/common/constants.d.ts +2 -1
  4. package/dist/Prebuilt/components/HMSVideo/HLSCaptionSelector.d.ts +5 -0
  5. package/dist/Prebuilt/components/Polls/Voting/Leaderboard.d.ts +4 -0
  6. package/dist/Prebuilt/components/Polls/Voting/LeaderboardEntry.d.ts +9 -0
  7. package/dist/Prebuilt/components/Polls/Voting/PeerParticipationSummary.d.ts +5 -0
  8. package/dist/{chunk-2B7YYNHQ.js → chunk-HEOH5H43.js} +6767 -1064
  9. package/dist/chunk-HEOH5H43.js.map +7 -0
  10. package/dist/index.cjs.js +7263 -1445
  11. package/dist/index.cjs.js.map +4 -4
  12. package/dist/index.js +1 -1
  13. package/dist/meta.cjs.json +335 -50
  14. package/dist/meta.esbuild.json +351 -65
  15. package/package.json +6 -6
  16. package/src/Prebuilt/common/PeersSorter.ts +12 -3
  17. package/src/Prebuilt/common/constants.ts +1 -0
  18. package/src/Prebuilt/common/utils.js +34 -0
  19. package/src/Prebuilt/components/Chat/Chat.jsx +3 -4
  20. package/src/Prebuilt/components/HMSVideo/HLSCaptionSelector.tsx +13 -0
  21. package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +34 -2
  22. package/src/Prebuilt/components/Notifications/Notifications.tsx +33 -2
  23. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +3 -9
  24. package/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx +21 -1
  25. package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.jsx +34 -7
  26. package/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.jsx +2 -2
  27. package/src/Prebuilt/components/Polls/Polls.tsx +3 -0
  28. package/src/Prebuilt/components/Polls/Voting/Leaderboard.tsx +115 -0
  29. package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +63 -0
  30. package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +38 -0
  31. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +28 -11
  32. package/src/Prebuilt/components/Polls/Voting/StandardVoting.jsx +7 -1
  33. package/src/Prebuilt/components/Polls/Voting/Voting.jsx +31 -13
  34. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +33 -21
  35. package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +47 -35
  36. package/src/Prebuilt/components/Polls/common/StatusIndicator.jsx +2 -22
  37. package/src/Prebuilt/components/Polls/common/VoteCount.jsx +1 -15
  38. package/src/Prebuilt/components/VideoLayouts/EqualProminence.tsx +6 -5
  39. package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +25 -6
  40. package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +0 -1
  41. package/src/Prebuilt/layouts/HLSView.jsx +51 -3
  42. package/dist/HLSView-662T7R7H.js.map +0 -7
  43. package/dist/chunk-2B7YYNHQ.js.map +0 -7
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import { HMSPoll, selectLocalPeer, useHMSStore } from '@100mslive/react-sdk';
3
+ import { Box } from '../../../../Layout';
4
+ import { Text } from '../../../../Text';
5
+ // @ts-ignore
6
+ import { getPeerParticipationSummary } from '../../../common/utils';
7
+
8
+ export const PeerParticipationSummary = ({ poll }: { poll: HMSPoll }) => {
9
+ const localPeer = useHMSStore(selectLocalPeer);
10
+ const { totalResponses, correctResponses, score } = getPeerParticipationSummary(
11
+ poll,
12
+ localPeer?.id,
13
+ localPeer?.customerUserId,
14
+ );
15
+
16
+ const boxes = [
17
+ { title: 'Points', value: score },
18
+ { title: 'Correct Answers', value: `${correctResponses}/${totalResponses}` },
19
+ ];
20
+ return (
21
+ <Box>
22
+ <Text css={{ fontWeight: '$semiBold', my: '$8' }}>Participation Summary</Text>
23
+ <Box css={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '$4' }}>
24
+ {boxes.map(box => (
25
+ <Box key={box.title} css={{ p: '$8', background: '$surface_default', borderRadius: '$1' }}>
26
+ <Text
27
+ variant="tiny"
28
+ css={{ textTransform: 'uppercase', color: '$on_surface_medium', fontWeight: '$semiBold', my: '$4' }}
29
+ >
30
+ {box.title}
31
+ </Text>
32
+ <Text css={{ fontWeight: '$semiBold' }}>{box.value}</Text>
33
+ </Box>
34
+ ))}
35
+ </Box>
36
+ </Box>
37
+ );
38
+ };
@@ -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;
@@ -113,14 +116,22 @@ export const QuestionCard = ({
113
116
  borderRadius: '$1',
114
117
  p: '$md',
115
118
  mt: '$md',
116
- border:
117
- isQuiz && localPeerResponse && !localPeerResponse.skipped
118
- ? `1px solid ${isCorrectAnswer ? '$alert_success' : '$alert_error_default'}`
119
- : 'none',
119
+ border: respondedToQuiz ? `1px solid ${isCorrectAnswer ? '$alert_success' : '$alert_error_default'}` : 'none',
120
120
  }}
121
121
  >
122
122
  <Flex align="center" justify="between">
123
- <Text variant="caption" css={{ color: '$on_surface_low', fontWeight: '$semiBold' }}>
123
+ <Text
124
+ variant="caption"
125
+ css={{
126
+ color: respondedToQuiz ? (isCorrectAnswer ? '$alert_success' : '$alert_error_default') : '$on_surface_low',
127
+ fontWeight: '$semiBold',
128
+ display: 'flex',
129
+ alignItems: 'center',
130
+ gap: '$4',
131
+ }}
132
+ >
133
+ {respondedToQuiz && isCorrectAnswer ? <CheckCircleIcon height={20} width={20} /> : null}
134
+ {respondedToQuiz && !isCorrectAnswer ? <CrossCircleIcon height={20} width={20} /> : null}
124
135
  QUESTION {index} OF {totalQuestions}: {type.toUpperCase()}
125
136
  </Text>
126
137
 
@@ -196,6 +207,8 @@ export const QuestionCard = ({
196
207
  setAnswer={setSingleOptionAnswer}
197
208
  totalResponses={result?.totalResponses}
198
209
  showVoteCount={showVoteCount}
210
+ localPeerResponse={localPeerResponse}
211
+ isStopped={pollState === 'stopped'}
199
212
  />
200
213
  ) : null}
201
214
 
@@ -211,6 +224,8 @@ export const QuestionCard = ({
211
224
  setSelectedOptions={setMultipleOptionAnswer}
212
225
  totalResponses={result?.totalResponses}
213
226
  showVoteCount={showVoteCount}
227
+ localPeerResponse={localPeerResponse}
228
+ isStopped={pollState === 'stopped'}
214
229
  />
215
230
  ) : null}
216
231
 
@@ -221,14 +236,14 @@ export const QuestionCard = ({
221
236
  onSkip={handleSkip}
222
237
  onVote={handleVote}
223
238
  response={localPeerResponse}
224
- stringAnswerExpected={stringAnswerExpected}
239
+ isQuiz={isQuiz}
225
240
  />
226
241
  )}
227
242
  </Box>
228
243
  );
229
244
  };
230
245
 
231
- const QuestionActions = ({ isValidVote, skippable, response, stringAnswerExpected, onVote, onSkip }) => {
246
+ const QuestionActions = ({ isValidVote, skippable, response, isQuiz, onVote, onSkip }) => {
232
247
  return (
233
248
  <Flex align="center" justify="end" css={{ gap: '$4', w: '100%' }}>
234
249
  {skippable && !response ? (
@@ -239,11 +254,13 @@ const QuestionActions = ({ isValidVote, skippable, response, stringAnswerExpecte
239
254
 
240
255
  {response ? (
241
256
  <Text css={{ fontWeight: '$semiBold', color: '$on_surface_medium' }}>
242
- {response.skipped ? 'Skipped' : stringAnswerExpected ? 'Submitted' : 'Voted'}
257
+ {response.skipped ? 'Skipped' : null}
258
+ {isQuiz && !response.skipped ? 'Answered' : null}
259
+ {!isQuiz && !response.skipped ? 'Voted' : null}
243
260
  </Text>
244
261
  ) : (
245
262
  <Button css={{ p: '$xs $10', fontWeight: '$semiBold' }} disabled={!isValidVote} onClick={onVote}>
246
- {stringAnswerExpected ? 'Submit' : 'Vote'}
263
+ {isQuiz ? 'Answer' : 'Vote'}
247
264
  </Button>
248
265
  )}
249
266
  </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;
@@ -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,
@@ -1,13 +1,20 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
2
  import { GridVideoTileLayout } from '@100mslive/types-prebuilt/elements/video_tile_layout';
3
- import { selectPeers, selectPeerScreenSharing, useHMSStore, useHMSVanillaStore } from '@100mslive/react-sdk';
3
+ import {
4
+ selectLocalPeerRoleName,
5
+ selectPeers,
6
+ selectPeerScreenSharing,
7
+ useHMSStore,
8
+ useHMSVanillaStore,
9
+ } from '@100mslive/react-sdk';
4
10
  import { EqualProminence } from './EqualProminence';
5
11
  import { RoleProminence } from './RoleProminence';
6
12
  import { ScreenshareLayout } from './ScreenshareLayout';
7
13
  // @ts-ignore: No implicit Any
8
- import { usePinnedTrack } from '../AppData/useUISettings';
14
+ import { usePinnedTrack, useSetAppDataByKey } from '../AppData/useUISettings';
9
15
  import { VideoTileContext } from '../hooks/useVideoTileLayout';
10
16
  import PeersSorter from '../../common/PeersSorter';
17
+ import { APP_DATA } from '../../common/constants';
11
18
 
12
19
  export type TileCustomisationProps = {
13
20
  hide_participant_name_on_tile: boolean;
@@ -34,6 +41,8 @@ export const GridLayout = ({
34
41
  const peerSharing = useHMSStore(selectPeerScreenSharing);
35
42
  const pinnedTrack = usePinnedTrack();
36
43
  const peers = useHMSStore(selectPeers);
44
+ const localPeerRole = useHMSStore(selectLocalPeerRoleName);
45
+ const [activeScreensharePeerId] = useSetAppDataByKey(APP_DATA.activeScreensharePeerId);
37
46
  const isRoleProminence =
38
47
  (prominentRoles.length &&
39
48
  peers.some(
@@ -41,11 +50,20 @@ export const GridLayout = ({
41
50
  )) ||
42
51
  pinnedTrack;
43
52
  const updatedPeers = useMemo(() => {
44
- if (isInsetEnabled && !isRoleProminence && !peerSharing) {
45
- return peers.filter(peer => !peer.isLocal);
53
+ // remove screenshare peer from active speaker sorting
54
+ if (activeScreensharePeerId) {
55
+ return peers.filter(peer => peer.id !== activeScreensharePeerId);
56
+ }
57
+ if (isInsetEnabled) {
58
+ // if localPeer role is prominent role, it shows up in the center, so allow it in active speaker sorting
59
+ if (localPeerRole && prominentRoles.includes(localPeerRole)) {
60
+ return peers;
61
+ } else {
62
+ return peers.filter(peer => !peer.isLocal);
63
+ }
46
64
  }
47
65
  return peers;
48
- }, [isInsetEnabled, isRoleProminence, peerSharing, peers]);
66
+ }, [isInsetEnabled, activeScreensharePeerId, localPeerRole, prominentRoles, peers]);
49
67
  const vanillaStore = useHMSVanillaStore();
50
68
  const [sortedPeers, setSortedPeers] = useState(updatedPeers);
51
69
  const peersSorter = useMemo(() => new PeersSorter(vanillaStore), [vanillaStore]);
@@ -61,7 +79,8 @@ export const GridLayout = ({
61
79
  };
62
80
 
63
81
  useEffect(() => {
64
- if (mainPage !== 0) {
82
+ if (mainPage !== 0 || pageSize === 0) {
83
+ setSortedPeers(updatedPeers);
65
84
  return;
66
85
  }
67
86
  peersSorter.setPeersAndTilesPerPage({
@@ -10,7 +10,6 @@ import { LayoutProps } from './interface';
10
10
  import { ProminenceLayout } from './ProminenceLayout';
11
11
  // @ts-ignore: No implicit Any
12
12
  import { useSetAppDataByKey } from '../AppData/useUISettings';
13
- // @ts-ignore: No implicit Any
14
13
  import { APP_DATA } from '../../common/constants';
15
14
 
16
15
  export const ScreenshareLayout = ({ peers, onPageChange, onPageSize, edgeToEdge }: LayoutProps) => {