@100mslive/roomkit-react 0.2.1 → 0.2.2-alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. package/dist/{HLSView-U7FQ2YLK.js → HLSView-2DSOSSTP.js} +19 -4
  2. package/dist/HLSView-2DSOSSTP.js.map +7 -0
  3. package/dist/Prebuilt/components/Polls/Voting/LeaderboardEntry.d.ts +2 -1
  4. package/dist/Prebuilt/components/RoleChangeRequest/RequestPrompt.d.ts +2 -1
  5. package/dist/Prebuilt/components/VirtualBackground/VBHandler.d.ts +1 -0
  6. package/dist/{chunk-GNQAOG73.js → chunk-XXVJPGLN.js} +203 -185
  7. package/dist/{chunk-GNQAOG73.js.map → chunk-XXVJPGLN.js.map} +3 -3
  8. package/dist/index.cjs.js +161 -125
  9. package/dist/index.cjs.js.map +3 -3
  10. package/dist/index.js +1 -1
  11. package/dist/meta.cjs.json +41 -41
  12. package/dist/meta.esbuild.json +51 -51
  13. package/package.json +6 -6
  14. package/src/Button/Button.tsx +2 -2
  15. package/src/Prebuilt/components/Notifications/PermissionErrorModal.tsx +8 -1
  16. package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +17 -5
  17. package/src/Prebuilt/components/Polls/Voting/LeaderboardSummary.tsx +1 -0
  18. package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +4 -2
  19. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +38 -81
  20. package/src/Prebuilt/components/Polls/Voting/StandardVoting.tsx +0 -1
  21. package/src/Prebuilt/components/Polls/Voting/TimedVoting.tsx +2 -3
  22. package/src/Prebuilt/components/Polls/Voting/Voting.tsx +7 -6
  23. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +1 -2
  24. package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +6 -4
  25. package/src/Prebuilt/components/Polls/common/StatusIndicator.tsx +1 -1
  26. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +4 -14
  27. package/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx +15 -5
  28. package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +3 -0
  29. package/src/Prebuilt/components/VirtualBackground/VBHandler.tsx +11 -1
  30. package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +16 -2
  31. package/src/Prebuilt/layouts/HLSView.jsx +16 -1
  32. package/src/Sheet/Sheet.tsx +1 -1
  33. package/dist/HLSView-U7FQ2YLK.js.map +0 -7
  34. package/src/Prebuilt/images/first_person.png +0 -0
@@ -1,14 +1,8 @@
1
1
  // @ts-check
2
- import React, { useCallback, useMemo, useState } from 'react';
2
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
3
3
  import { selectLocalPeer, selectLocalPeerRoleName, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
4
- import {
5
- CheckCircleIcon,
6
- ChevronDownIcon,
7
- ChevronLeftIcon,
8
- ChevronRightIcon,
9
- CrossCircleIcon,
10
- } from '@100mslive/react-icons';
11
- import { Box, Button, Flex, IconButton, Text } from '../../../../';
4
+ import { CheckCircleIcon, ChevronDownIcon, CrossCircleIcon } from '@100mslive/react-icons';
5
+ import { Box, Button, Flex, Text } from '../../../../';
12
6
  import { checkCorrectAnswer } from '../../../common/utils';
13
7
  import { MultipleChoiceOptions } from '../common/MultipleChoiceOptions';
14
8
  import { SingleChoiceOptions } from '../common/SingleChoiceOptions';
@@ -27,9 +21,7 @@ export const QuestionCard = ({
27
21
  options = [],
28
22
  answer,
29
23
  setCurrentIndex,
30
- skippable = false,
31
24
  responses = [],
32
- isTimed = false,
33
25
  rolesThatCanViewResponses,
34
26
  }) => {
35
27
  const actions = useHMSActions();
@@ -37,6 +29,7 @@ export const QuestionCard = ({
37
29
  const localPeerResponse = responses?.find(
38
30
  response => response.peer?.peerid === localPeer?.id || response.peer?.userid === localPeer?.customerUserId,
39
31
  );
32
+
40
33
  const isLocalPeerCreator = localPeer?.id === startedBy;
41
34
  const localPeerRoleName = useHMSStore(selectLocalPeerRoleName);
42
35
  const roleCanViewResponse =
@@ -48,27 +41,13 @@ export const QuestionCard = ({
48
41
 
49
42
  const isLive = pollState === 'started';
50
43
  const canRespond = isLive && !localPeerResponse;
51
-
44
+ const startTime = useRef(Date.now());
52
45
  const isCorrectAnswer = checkCorrectAnswer(answer, localPeerResponse, type);
53
46
 
54
- const prev = index !== 1;
55
- const next = index !== totalQuestions && (skippable || localPeerResponse);
56
-
57
- const moveNext = useCallback(() => {
58
- setCurrentIndex(curr => Math.min(totalQuestions, curr + 1));
59
- }, [setCurrentIndex, totalQuestions]);
60
-
61
- const movePrev = () => {
62
- setCurrentIndex(curr => Math.max(1, curr - 1));
63
- };
64
-
65
- // const [textAnswer, setTextAnswer] = useState('');
66
47
  const [singleOptionAnswer, setSingleOptionAnswer] = useState();
67
48
  const [multipleOptionAnswer, setMultipleOptionAnswer] = useState(new Set());
68
49
  const [showOptions, setShowOptions] = useState(true);
69
50
 
70
- // const stringAnswerExpected = [QUESTION_TYPE.LONG_ANSWER, QUESTION_TYPE.SHORT_ANSWER].includes(type);
71
-
72
51
  const respondedToQuiz = isQuiz && localPeerResponse && !localPeerResponse.skipped;
73
52
 
74
53
  const isValidVote = useMemo(() => {
@@ -83,24 +62,31 @@ export const QuestionCard = ({
83
62
  if (!isValidVote) {
84
63
  return;
85
64
  }
65
+
86
66
  await actions.interactivityCenter.addResponsesToPoll(pollID, [
87
67
  {
88
68
  questionIndex: index,
89
69
  option: singleOptionAnswer,
90
70
  options: Array.from(multipleOptionAnswer),
71
+ duration: Date.now() - startTime.current,
91
72
  },
92
73
  ]);
93
- }, [actions, index, pollID, isValidVote, singleOptionAnswer, multipleOptionAnswer]);
74
+ startTime.current = Date.now();
94
75
 
95
- const handleSkip = useCallback(async () => {
96
- await actions.interactivityCenter.addResponsesToPoll(pollID, [
97
- {
98
- questionIndex: index,
99
- skipped: true,
100
- },
101
- ]);
102
- moveNext();
103
- }, [actions, index, pollID, moveNext]);
76
+ if (isQuiz && index !== totalQuestions) {
77
+ setSingleOptionAnswer(undefined);
78
+ setMultipleOptionAnswer(new Set());
79
+ }
80
+ }, [
81
+ isValidVote,
82
+ actions.interactivityCenter,
83
+ pollID,
84
+ index,
85
+ singleOptionAnswer,
86
+ multipleOptionAnswer,
87
+ totalQuestions,
88
+ isQuiz,
89
+ ]);
104
90
 
105
91
  return (
106
92
  <Box
@@ -135,39 +121,6 @@ export const QuestionCard = ({
135
121
  {respondedToQuiz && !isCorrectAnswer ? <CrossCircleIcon height={20} width={20} /> : null}
136
122
  QUESTION {index} OF {totalQuestions}: {type.toUpperCase()}
137
123
  </Text>
138
-
139
- {isTimed ? (
140
- <Flex align="center" css={{ gap: '$4' }}>
141
- <IconButton
142
- disabled={!prev}
143
- onClick={movePrev}
144
- css={
145
- prev
146
- ? { color: '$on_surface_high', cursor: 'pointer' }
147
- : {
148
- color: '$on_surface_low',
149
- cursor: 'not-allowed',
150
- }
151
- }
152
- >
153
- <ChevronLeftIcon height={16} width={16} />
154
- </IconButton>
155
- <IconButton
156
- disabled={!next}
157
- onClick={moveNext}
158
- css={
159
- next
160
- ? { color: '$on_surface_high', cursor: 'pointer' }
161
- : {
162
- color: '$on_surface_low',
163
- cursor: 'not-allowed',
164
- }
165
- }
166
- >
167
- <ChevronRightIcon height={16} width={16} />
168
- </IconButton>
169
- </Flex>
170
- ) : null}
171
124
  </Flex>
172
125
 
173
126
  <Flex justify="between" css={{ my: '$md' }}>
@@ -185,10 +138,10 @@ export const QuestionCard = ({
185
138
  <Box css={{ maxHeight: showOptions ? '$80' : '0', transition: 'max-height 0.3s ease', overflowY: 'hidden' }}>
186
139
  {type === QUESTION_TYPE.SINGLE_CHOICE ? (
187
140
  <SingleChoiceOptions
141
+ key={index}
188
142
  questionIndex={index}
189
143
  isQuiz={isQuiz}
190
144
  canRespond={canRespond}
191
- response={localPeerResponse}
192
145
  correctOptionIndex={answer?.option}
193
146
  options={options}
194
147
  setAnswer={setSingleOptionAnswer}
@@ -196,14 +149,15 @@ export const QuestionCard = ({
196
149
  showVoteCount={showVoteCount}
197
150
  localPeerResponse={localPeerResponse}
198
151
  isStopped={pollState === 'stopped'}
152
+ answer={singleOptionAnswer}
199
153
  />
200
154
  ) : null}
155
+
201
156
  {type === QUESTION_TYPE.MULTIPLE_CHOICE ? (
202
157
  <MultipleChoiceOptions
203
158
  questionIndex={index}
204
159
  isQuiz={isQuiz}
205
160
  canRespond={canRespond}
206
- response={localPeerResponse}
207
161
  correctOptionIndexes={answer?.options}
208
162
  options={options}
209
163
  selectedOptions={multipleOptionAnswer}
@@ -214,14 +168,16 @@ export const QuestionCard = ({
214
168
  isStopped={pollState === 'stopped'}
215
169
  />
216
170
  ) : null}
171
+
217
172
  {isLive && (
218
173
  <QuestionActions
219
174
  isValidVote={isValidVote}
220
- skippable={skippable}
221
- onSkip={handleSkip}
222
175
  onVote={handleVote}
223
176
  response={localPeerResponse}
224
177
  isQuiz={isQuiz}
178
+ incrementIndex={() => {
179
+ setCurrentIndex(curr => Math.min(totalQuestions, curr + 1));
180
+ }}
225
181
  />
226
182
  )}
227
183
  </Box>
@@ -229,15 +185,9 @@ export const QuestionCard = ({
229
185
  );
230
186
  };
231
187
 
232
- const QuestionActions = ({ isValidVote, skippable, response, isQuiz, onVote, onSkip }) => {
188
+ const QuestionActions = ({ isValidVote, response, isQuiz, onVote, incrementIndex }) => {
233
189
  return (
234
190
  <Flex align="center" justify="end" css={{ gap: '$4', w: '100%' }}>
235
- {skippable && !response ? (
236
- <Button variant="standard" onClick={onSkip} css={{ p: '$xs $10', fontWeight: '$semiBold' }}>
237
- Skip
238
- </Button>
239
- ) : null}
240
-
241
191
  {response ? (
242
192
  <Text css={{ fontWeight: '$semiBold', color: '$on_surface_medium' }}>
243
193
  {response.skipped ? 'Skipped' : null}
@@ -245,7 +195,14 @@ const QuestionActions = ({ isValidVote, skippable, response, isQuiz, onVote, onS
245
195
  {!isQuiz && !response.skipped ? 'Voted' : null}
246
196
  </Text>
247
197
  ) : (
248
- <Button css={{ p: '$xs $10', fontWeight: '$semiBold' }} disabled={!isValidVote} onClick={onVote}>
198
+ <Button
199
+ css={{ p: '$xs $10', fontWeight: '$semiBold' }}
200
+ disabled={!isValidVote}
201
+ onClick={() => {
202
+ onVote();
203
+ incrementIndex();
204
+ }}
205
+ >
249
206
  {isQuiz ? 'Answer' : 'Vote'}
250
207
  </Button>
251
208
  )}
@@ -28,7 +28,6 @@ export const StandardView = ({ poll }: { poll: HMSPoll }) => {
28
28
  result={question.result}
29
29
  totalQuestions={poll.questions?.length || 0}
30
30
  options={question.options}
31
- skippable={question.skippable}
32
31
  responses={question.responses}
33
32
  answer={question.answer}
34
33
  setCurrentIndex={() => {
@@ -4,9 +4,10 @@ import { HMSPoll } from '@100mslive/react-sdk';
4
4
  import { QuestionCard } from './QuestionCard';
5
5
 
6
6
  export const TimedView = ({ poll }: { poll: HMSPoll }) => {
7
- // backend question index starts at 1
7
+ // Backend question index starts at 1
8
8
  const [currentIndex, setCurrentIndex] = useState(1);
9
9
  const activeQuestion = poll.questions?.find(question => question.index === currentIndex);
10
+
10
11
  if (!activeQuestion) {
11
12
  return null;
12
13
  }
@@ -23,12 +24,10 @@ export const TimedView = ({ poll }: { poll: HMSPoll }) => {
23
24
  result={activeQuestion?.result}
24
25
  totalQuestions={poll.questions?.length || 0}
25
26
  options={activeQuestion.options}
26
- skippable={activeQuestion.skippable || false}
27
27
  responses={activeQuestion.responses}
28
28
  answer={activeQuestion.answer}
29
29
  setCurrentIndex={setCurrentIndex}
30
30
  rolesThatCanViewResponses={poll.rolesThatCanViewResponses}
31
- isTimed
32
31
  />
33
32
  );
34
33
  };
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import {
3
- selectLocalPeerID,
4
3
  selectPeerNameByID,
4
+ selectPermissions,
5
5
  selectPollByID,
6
6
  useHMSActions,
7
7
  useHMSStore,
@@ -21,8 +21,11 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
21
21
  const actions = useHMSActions();
22
22
  const poll = useHMSStore(selectPollByID(id));
23
23
  const pollCreatorName = useHMSStore(selectPeerNameByID(poll?.createdBy));
24
- const isLocalPeerCreator = useHMSStore(selectLocalPeerID) === poll?.createdBy;
24
+ const permissions = useHMSStore(selectPermissions);
25
+ const canEndActivity = !!permissions?.pollWrite;
25
26
  const { setPollView } = usePollViewState();
27
+ // Sets view - linear or vertical, toggles timer indicator
28
+ const showSingleView = poll?.type === 'quiz' && poll.state === 'started';
26
29
 
27
30
  if (!poll) {
28
31
  return null;
@@ -30,8 +33,6 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
30
33
 
31
34
  const canViewLeaderboard = poll.type === 'quiz' && poll.state === 'stopped' && !poll.anonymous;
32
35
 
33
- // Sets view - linear or vertical, toggles timer indicator
34
- const isTimed = (poll.duration || 0) > 0;
35
36
  const isLive = poll.state === 'started';
36
37
 
37
38
  return (
@@ -74,9 +75,9 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
74
75
  </Text>
75
76
  ) : null}
76
77
 
77
- {isTimed ? <TimedView poll={poll} /> : <StandardView poll={poll} />}
78
+ {showSingleView ? <TimedView poll={poll} /> : <StandardView poll={poll} />}
78
79
 
79
- {poll.state === 'started' && isLocalPeerCreator && (
80
+ {poll.state === 'started' && canEndActivity && (
80
81
  <Button
81
82
  variant="danger"
82
83
  css={{ fontWeight: '$semiBold', w: 'max-content', ml: 'auto', mt: '$8' }}
@@ -10,7 +10,6 @@ export const MultipleChoiceOptions = ({
10
10
  questionIndex,
11
11
  options,
12
12
  canRespond,
13
- response,
14
13
  totalResponses,
15
14
  selectedOptions,
16
15
  setSelectedOptions,
@@ -39,7 +38,7 @@ export const MultipleChoiceOptions = ({
39
38
  <Checkbox.Root
40
39
  id={`${questionIndex}-${option.index}`}
41
40
  disabled={!canRespond}
42
- checked={response?.options?.includes(option.index)}
41
+ checked={localPeerResponse?.options?.includes(option.index)}
43
42
  onCheckedChange={checked => handleCheckedChange(checked, option.index)}
44
43
  css={{
45
44
  cursor: canRespond ? 'pointer' : 'not-allowed',
@@ -9,7 +9,6 @@ import { VoteProgress } from './VoteProgress';
9
9
  export const SingleChoiceOptions = ({
10
10
  questionIndex,
11
11
  options,
12
- response,
13
12
  canRespond,
14
13
  setAnswer,
15
14
  totalResponses,
@@ -18,9 +17,10 @@ export const SingleChoiceOptions = ({
18
17
  isStopped,
19
18
  isQuiz,
20
19
  localPeerResponse,
20
+ answer,
21
21
  }) => {
22
22
  return (
23
- <RadioGroup.Root value={response?.option} onValueChange={value => setAnswer(value)}>
23
+ <RadioGroup.Root value={answer || null} onValueChange={value => setAnswer(value)}>
24
24
  <Flex direction="column" css={{ gap: '$md', w: '100%', mb: '$md' }}>
25
25
  {options.map(option => {
26
26
  return (
@@ -65,8 +65,10 @@ export const SingleChoiceOptions = ({
65
65
 
66
66
  <Flex direction="column" css={{ flexGrow: '1' }}>
67
67
  <Flex css={{ w: '100%' }}>
68
- <Text css={{ display: 'flex', flexGrow: '1' }}>
69
- <Label htmlFor={`${questionIndex}-${option.index}`}>{option.text}</Label>
68
+ <Text css={{ display: 'flex', flexGrow: '1', color: '$on_surface_high' }}>
69
+ <Label style={{ color: 'inherit' }} htmlFor={`${questionIndex}-${option.index}`}>
70
+ {option.text}
71
+ </Label>
70
72
  </Text>
71
73
  {showVoteCount && <VoteCount voteCount={option.voteCount} />}
72
74
  </Flex>
@@ -15,7 +15,7 @@ export const StatusIndicator = ({ isLive }: { isLive: boolean }) => {
15
15
  variant="caption"
16
16
  css={{
17
17
  fontWeight: '$semiBold',
18
- color: '$on_surface_high',
18
+ color: '$on_primary_high',
19
19
  }}
20
20
  >
21
21
  {isLive ? 'LIVE' : 'ENDED'}
@@ -160,20 +160,7 @@ const PreviewJoin = ({
160
160
  <Chip content={getParticipantChipContent(peerCount)} hideIfNoContent />
161
161
  </Flex>
162
162
  </Flex>
163
- {toggleVideo ? (
164
- <Flex
165
- align="center"
166
- justify="center"
167
- css={{
168
- mt: '$14',
169
- '@md': { mt: 0 },
170
- '@sm': { width: '100%' },
171
- flexDirection: 'column',
172
- }}
173
- >
174
- <PreviewTile name={name} error={previewError} />
175
- </Flex>
176
- ) : null}
163
+ {toggleVideo ? <PreviewTile name={name} error={previewError} /> : null}
177
164
  <Box css={{ w: '100%', maxWidth: `${Math.max(aspectRatio, 1) * 360}px` }}>
178
165
  <PreviewControls hideSettings={!toggleVideo && !toggleAudio} vbEnabled={!!virtual_background} />
179
166
  <PreviewForm
@@ -225,9 +212,12 @@ export const PreviewTile = ({ name, error }: { name: string; error?: boolean })
225
212
  bg: '$surface_default',
226
213
  aspectRatio,
227
214
  height: 'min(360px, 70vh)',
215
+ width: 'auto',
228
216
  maxWidth: '640px',
229
217
  overflow: 'clip',
218
+ mt: '$14',
230
219
  '@md': {
220
+ mt: 0,
231
221
  width: 'min(220px, 70vw)',
232
222
  maxWidth: '100%',
233
223
  my: '$4',
@@ -10,6 +10,7 @@ export const RequestPrompt = ({
10
10
  body,
11
11
  actionText = 'Accept',
12
12
  onAction,
13
+ disableActions = false,
13
14
  }: {
14
15
  open?: boolean;
15
16
  onOpenChange: (value: boolean) => void;
@@ -17,6 +18,7 @@ export const RequestPrompt = ({
17
18
  body: React.ReactNode;
18
19
  actionText?: string;
19
20
  onAction: () => void;
21
+ disableActions?: boolean;
20
22
  }) => {
21
23
  const isMobile = useMedia(cssConfig.media.md);
22
24
 
@@ -26,7 +28,7 @@ export const RequestPrompt = ({
26
28
  <Sheet.Content css={{ py: '$8' }}>
27
29
  <Text css={{ fontWeight: '$semiBold', c: '$on_surface_high', '@md': { px: '$8' } }}>{title}</Text>
28
30
  {body}
29
- <RequestActions actionText={actionText} onAction={onAction} />
31
+ <RequestActions actionText={actionText} onAction={onAction} disabled={disableActions} />
30
32
  </Sheet.Content>
31
33
  </Sheet.Root>
32
34
  );
@@ -40,24 +42,32 @@ export const RequestPrompt = ({
40
42
  <Text variant="h6">{title}</Text>
41
43
  </Dialog.Title>
42
44
  <Box css={{ mt: '$4', mb: '$10' }}>{body}</Box>
43
- <RequestActions actionText={actionText} onAction={onAction} />
45
+ <RequestActions actionText={actionText} onAction={onAction} disabled={disableActions} />
44
46
  </Dialog.Content>
45
47
  </Dialog.Portal>
46
48
  </Dialog.Root>
47
49
  );
48
50
  };
49
51
 
50
- const RequestActions = ({ onAction, actionText }: { actionText?: string; onAction: () => void }) => (
52
+ const RequestActions = ({
53
+ onAction,
54
+ actionText,
55
+ disabled = false,
56
+ }: {
57
+ actionText?: string;
58
+ onAction: () => void;
59
+ disabled?: boolean;
60
+ }) => (
51
61
  <Flex justify="center" align="center" css={{ width: '100%', gap: '$md', '@md': { mt: '$8', px: '$8' } }}>
52
62
  <Box css={{ width: '50%' }}>
53
63
  <Dialog.Close css={{ width: '100%' }}>
54
- <Button variant="standard" outlined css={{ width: '100%' }}>
64
+ <Button variant="standard" outlined css={{ width: '100%' }} disabled={disabled}>
55
65
  Decline
56
66
  </Button>
57
67
  </Dialog.Close>
58
68
  </Box>
59
69
  <Box css={{ width: '50%' }}>
60
- <Button variant="primary" css={{ width: '100%' }} onClick={onAction}>
70
+ <Button variant="primary" css={{ width: '100%' }} onClick={onAction} disabled={disabled}>
61
71
  {actionText}
62
72
  </Button>
63
73
  </Box>
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect } from 'react';
2
2
  import {
3
+ selectIsInPreview,
3
4
  selectLocalPeerName,
4
5
  selectLocalPeerRoleName,
5
6
  selectRoleChangeRequest,
@@ -20,6 +21,7 @@ import { ROLE_CHANGE_DECLINED } from '../../common/constants';
20
21
  export const RoleChangeRequestModal = () => {
21
22
  const hmsActions = useHMSActions();
22
23
  const { updateMetaData } = useMyMetadata();
24
+ const isPreview = useHMSStore(selectIsInPreview);
23
25
  const currentRole = useHMSStore(selectLocalPeerRoleName);
24
26
  const roleChangeRequest = useHMSStore(selectRoleChangeRequest);
25
27
  const name = useHMSStore(selectLocalPeerName);
@@ -87,6 +89,7 @@ export const RoleChangeRequestModal = () => {
87
89
  await hmsActions.lowerLocalPeerHand();
88
90
  }}
89
91
  actionText="Accept"
92
+ disableActions={!isPreview}
90
93
  />
91
94
  );
92
95
  };
@@ -19,8 +19,18 @@ export class VBPlugin {
19
19
  if (this.effectsPlugin) {
20
20
  return this.effectsPlugin?.getBackground();
21
21
  } else {
22
+ const background = this.hmsPlugin?.getBackground();
22
23
  // @ts-ignore
23
- return this.hmsPlugin?.background?.src || this.hmsPlugin?.background;
24
+ return background?.src || background;
25
+ }
26
+ };
27
+
28
+ getBlurAmount = () => {
29
+ if (this.effectsPlugin) {
30
+ return this.effectsPlugin.getBlurAmount();
31
+ } else {
32
+ // Treating HMS VB intensity as a fixed value
33
+ return this.hmsPlugin?.getBackground() === HMSVirtualBackgroundTypes.BLUR ? 1 : 0;
24
34
  }
25
35
  };
26
36
 
@@ -14,7 +14,7 @@ import {
14
14
  useHMSStore,
15
15
  } from '@100mslive/react-sdk';
16
16
  import { BlurPersonHighIcon, CloseIcon, CrossCircleIcon } from '@100mslive/react-icons';
17
- import { Box, Flex, Video } from '../../../index';
17
+ import { Box, Flex, Slider, Video } from '../../../index';
18
18
  import { Text } from '../../../Text';
19
19
  import { VBCollection } from './VBCollection';
20
20
  import { VBHandler } from './VBHandler';
@@ -36,6 +36,7 @@ export const VBPicker = ({ backgroundMedia = [] }: { backgroundMedia: VirtualBac
36
36
  const mirrorLocalVideo = useUISettings(UI_SETTINGS.mirrorLocalVideo);
37
37
  const trackSelector = selectVideoTrackByID(localPeer?.videoTrack);
38
38
  const track = useHMSStore(trackSelector);
39
+ const [blurAmount, setBlurAmount] = useState(VBHandler.getBlurAmount() || 0.5);
39
40
  const roomState = useHMSStore(selectRoomState);
40
41
  const isLargeRoom = useHMSStore(selectIsLargeRoom);
41
42
  const isEffectsEnabled = useHMSStore(selectIsEffectsEnabled);
@@ -127,7 +128,7 @@ export const VBPicker = ({ backgroundMedia = [] }: { backgroundMedia: VirtualBac
127
128
  icon: <BlurPersonHighIcon style={iconDims} />,
128
129
  value: HMSVirtualBackgroundTypes.BLUR,
129
130
  onClick: async () => {
130
- await VBHandler?.setBlur(0.5);
131
+ await VBHandler?.setBlur(blurAmount);
131
132
  setActiveBackground(HMSVirtualBackgroundTypes.BLUR);
132
133
  },
133
134
  },
@@ -135,6 +136,19 @@ export const VBPicker = ({ backgroundMedia = [] }: { backgroundMedia: VirtualBac
135
136
  activeBackground={activeBackground}
136
137
  />
137
138
 
139
+ {activeBackground === HMSVirtualBackgroundTypes.BLUR && isEffectsEnabled && effectsKey ? (
140
+ <Slider
141
+ value={[blurAmount]}
142
+ onValueChange={async e => {
143
+ setBlurAmount(e[0]);
144
+ await VBHandler.setBlur(e[0]);
145
+ }}
146
+ step={0.1}
147
+ min={0.1}
148
+ max={1}
149
+ />
150
+ ) : null}
151
+
138
152
  <VBCollection
139
153
  title="Backgrounds"
140
154
  options={mediaList.map(mediaURL => ({
@@ -3,11 +3,13 @@ import { useFullscreen, useMedia, usePrevious, useToggle } from 'react-use';
3
3
  import { HLSPlaybackState, HMSHLSPlayer, HMSHLSPlayerEvents } from '@100mslive/hls-player';
4
4
  import screenfull from 'screenfull';
5
5
  import {
6
+ HMSNotificationTypes,
6
7
  selectAppData,
7
8
  selectHLSState,
8
9
  selectPeerNameByID,
9
10
  selectPollByID,
10
11
  useHMSActions,
12
+ useHMSNotifications,
11
13
  useHMSStore,
12
14
  useHMSVanillaStore,
13
15
  } from '@100mslive/react-sdk';
@@ -30,12 +32,14 @@ import { usePollViewToggle } from '../components/AppData/useSidepane';
30
32
  import { APP_DATA, EMOJI_REACTION_TYPE } from '../common/constants';
31
33
 
32
34
  let hlsPlayer;
35
+ const toastMap = {};
33
36
 
34
37
  const HLSView = () => {
35
38
  const videoRef = useRef(null);
36
39
  const hlsViewRef = useRef(null);
37
40
  const hlsState = useHMSStore(selectHLSState);
38
41
  const enablHlsStats = useHMSStore(selectAppData(APP_DATA.hlsStats));
42
+ const notification = useHMSNotifications(HMSNotificationTypes.POLL_STOPPED);
39
43
  const hmsActions = useHMSActions();
40
44
  const { themeType, theme } = useTheme();
41
45
  const [streamEnded, setStreamEnded] = useState(false);
@@ -85,6 +89,15 @@ const HLSView = () => {
85
89
  }
86
90
  }, [hlsUrl, streamEnded, lastHlsUrl]);
87
91
 
92
+ useEffect(() => {
93
+ if (!notification) return;
94
+ const toastID = toastMap?.[notification.data.id];
95
+ if (toastID) {
96
+ ToastManager.removeToast(toastMap[notification.data.id]);
97
+ delete toastMap[notification.data.id];
98
+ }
99
+ }, [notification]);
100
+
88
101
  useEffect(() => {
89
102
  const videoElem = videoRef.current;
90
103
  const setStreamEndedCallback = () => {
@@ -126,7 +139,7 @@ const HLSView = () => {
126
139
  const poll = vanillaStore.getState(selectPollByID(pollId));
127
140
  const pollStartedBy = vanillaStore.getState(selectPeerNameByID(poll.startedBy)) || 'Participant';
128
141
  // launch poll
129
- ToastManager.addToast({
142
+ const toastID = ToastManager.addToast({
130
143
  title: `${pollStartedBy} started a ${poll.type}: ${poll.title}`,
131
144
  action: (
132
145
  <Button
@@ -142,7 +155,9 @@ const HLSView = () => {
142
155
  {poll.type === 'quiz' ? 'Answer' : 'Vote'}
143
156
  </Button>
144
157
  ),
158
+ duration: Infinity,
145
159
  });
160
+ toastMap[pollId] = toastID;
146
161
  return;
147
162
  }
148
163
  switch (parsedPayload.type) {
@@ -35,7 +35,7 @@ const StyledContent = styled(DialogPrimitive.Content, {
35
35
  borderTopRightRadius: '$3',
36
36
  boxShadow: '0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)',
37
37
  position: 'fixed',
38
- zIndex: 1001,
38
+ zIndex: 22,
39
39
  top: 0,
40
40
  right: 0,
41
41
  left: 0,