@100mslive/roomkit-react 0.1.14-alpha.2 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. package/dist/{HLSView-3RARRZJO.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-W76VLHN6.js → chunk-HEOH5H43.js} +6808 -1105
  9. package/dist/chunk-HEOH5H43.js.map +7 -0
  10. package/dist/index.cjs.js +7305 -1487
  11. package/dist/index.cjs.js.map +4 -4
  12. package/dist/index.js +1 -1
  13. package/dist/meta.cjs.json +338 -53
  14. package/dist/meta.esbuild.json +354 -68
  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-3RARRZJO.js.map +0 -7
  43. package/dist/chunk-W76VLHN6.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) => {