@100mslive/roomkit-react 0.2.2-alpha.2 → 0.2.2-alpha.4
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/{HLSView-DJGDJPXM.js → HLSView-DPM6YJWZ.js} +19 -4
- package/dist/HLSView-DPM6YJWZ.js.map +7 -0
- package/dist/Prebuilt/components/Polls/Voting/LeaderboardEntry.d.ts +2 -1
- package/dist/Prebuilt/components/RoleChangeRequest/RequestPrompt.d.ts +2 -1
- package/dist/Prebuilt/components/VirtualBackground/VBHandler.d.ts +1 -0
- package/dist/{chunk-ERKB3GGI.js → chunk-S5NNUR2M.js} +230 -194
- package/dist/{chunk-ERKB3GGI.js.map → chunk-S5NNUR2M.js.map} +3 -3
- package/dist/index.cjs.js +189 -135
- package/dist/index.cjs.js.map +3 -3
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +46 -46
- package/dist/meta.esbuild.json +56 -56
- package/package.json +6 -6
- package/src/Button/Button.tsx +2 -2
- package/src/Prebuilt/components/Notifications/Notifications.tsx +14 -1
- package/src/Prebuilt/components/Notifications/PermissionErrorModal.tsx +8 -1
- package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +3 -4
- package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +17 -5
- package/src/Prebuilt/components/Polls/Voting/LeaderboardSummary.tsx +1 -0
- package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +5 -3
- package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +41 -83
- package/src/Prebuilt/components/Polls/Voting/StandardVoting.tsx +0 -1
- package/src/Prebuilt/components/Polls/Voting/TimedVoting.tsx +2 -3
- package/src/Prebuilt/components/Polls/Voting/Voting.tsx +7 -6
- package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +1 -2
- package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +6 -4
- package/src/Prebuilt/components/Polls/common/StatusIndicator.tsx +1 -1
- package/src/Prebuilt/components/Preview/PreviewJoin.tsx +4 -14
- package/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx +15 -5
- package/src/Prebuilt/components/RoleChangeRequest/RoleChangeRequestModal.tsx +3 -0
- package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +6 -1
- package/src/Prebuilt/components/VirtualBackground/VBHandler.tsx +11 -1
- package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +16 -2
- package/src/Prebuilt/layouts/HLSView.jsx +16 -1
- package/src/Sheet/Sheet.tsx +1 -1
- package/dist/HLSView-DJGDJPXM.js.map +0 -7
- 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
|
-
|
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 =
|
@@ -47,28 +40,15 @@ export const QuestionCard = ({
|
|
47
40
|
roleCanViewResponse && (localPeerResponse || (isLocalPeerCreator && pollState === 'stopped')) && !isQuiz;
|
48
41
|
|
49
42
|
const isLive = pollState === 'started';
|
43
|
+
const pollEnded = pollState === 'stopped';
|
50
44
|
const canRespond = isLive && !localPeerResponse;
|
51
|
-
|
45
|
+
const startTime = useRef(Date.now());
|
52
46
|
const isCorrectAnswer = checkCorrectAnswer(answer, localPeerResponse, type);
|
53
47
|
|
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
48
|
const [singleOptionAnswer, setSingleOptionAnswer] = useState();
|
67
49
|
const [multipleOptionAnswer, setMultipleOptionAnswer] = useState(new Set());
|
68
50
|
const [showOptions, setShowOptions] = useState(true);
|
69
51
|
|
70
|
-
// const stringAnswerExpected = [QUESTION_TYPE.LONG_ANSWER, QUESTION_TYPE.SHORT_ANSWER].includes(type);
|
71
|
-
|
72
52
|
const respondedToQuiz = isQuiz && localPeerResponse && !localPeerResponse.skipped;
|
73
53
|
|
74
54
|
const isValidVote = useMemo(() => {
|
@@ -83,24 +63,31 @@ export const QuestionCard = ({
|
|
83
63
|
if (!isValidVote) {
|
84
64
|
return;
|
85
65
|
}
|
66
|
+
|
86
67
|
await actions.interactivityCenter.addResponsesToPoll(pollID, [
|
87
68
|
{
|
88
69
|
questionIndex: index,
|
89
70
|
option: singleOptionAnswer,
|
90
71
|
options: Array.from(multipleOptionAnswer),
|
72
|
+
duration: Date.now() - startTime.current,
|
91
73
|
},
|
92
74
|
]);
|
93
|
-
|
75
|
+
startTime.current = Date.now();
|
94
76
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
77
|
+
if (isQuiz && index !== totalQuestions) {
|
78
|
+
setSingleOptionAnswer(undefined);
|
79
|
+
setMultipleOptionAnswer(new Set());
|
80
|
+
}
|
81
|
+
}, [
|
82
|
+
isValidVote,
|
83
|
+
actions.interactivityCenter,
|
84
|
+
pollID,
|
85
|
+
index,
|
86
|
+
singleOptionAnswer,
|
87
|
+
multipleOptionAnswer,
|
88
|
+
totalQuestions,
|
89
|
+
isQuiz,
|
90
|
+
]);
|
104
91
|
|
105
92
|
return (
|
106
93
|
<Box
|
@@ -131,43 +118,10 @@ export const QuestionCard = ({
|
|
131
118
|
gap: '$4',
|
132
119
|
}}
|
133
120
|
>
|
134
|
-
{respondedToQuiz && isCorrectAnswer ? <CheckCircleIcon height={20} width={20} /> : null}
|
135
|
-
{respondedToQuiz && !isCorrectAnswer ? <CrossCircleIcon height={20} width={20} /> : null}
|
121
|
+
{respondedToQuiz && isCorrectAnswer && pollEnded ? <CheckCircleIcon height={20} width={20} /> : null}
|
122
|
+
{respondedToQuiz && !isCorrectAnswer && pollEnded ? <CrossCircleIcon height={20} width={20} /> : null}
|
136
123
|
QUESTION {index} OF {totalQuestions}: {type.toUpperCase()}
|
137
124
|
</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
125
|
</Flex>
|
172
126
|
|
173
127
|
<Flex justify="between" css={{ my: '$md' }}>
|
@@ -185,10 +139,10 @@ export const QuestionCard = ({
|
|
185
139
|
<Box css={{ maxHeight: showOptions ? '$80' : '0', transition: 'max-height 0.3s ease', overflowY: 'hidden' }}>
|
186
140
|
{type === QUESTION_TYPE.SINGLE_CHOICE ? (
|
187
141
|
<SingleChoiceOptions
|
142
|
+
key={index}
|
188
143
|
questionIndex={index}
|
189
144
|
isQuiz={isQuiz}
|
190
145
|
canRespond={canRespond}
|
191
|
-
response={localPeerResponse}
|
192
146
|
correctOptionIndex={answer?.option}
|
193
147
|
options={options}
|
194
148
|
setAnswer={setSingleOptionAnswer}
|
@@ -196,14 +150,15 @@ export const QuestionCard = ({
|
|
196
150
|
showVoteCount={showVoteCount}
|
197
151
|
localPeerResponse={localPeerResponse}
|
198
152
|
isStopped={pollState === 'stopped'}
|
153
|
+
answer={singleOptionAnswer}
|
199
154
|
/>
|
200
155
|
) : null}
|
156
|
+
|
201
157
|
{type === QUESTION_TYPE.MULTIPLE_CHOICE ? (
|
202
158
|
<MultipleChoiceOptions
|
203
159
|
questionIndex={index}
|
204
160
|
isQuiz={isQuiz}
|
205
161
|
canRespond={canRespond}
|
206
|
-
response={localPeerResponse}
|
207
162
|
correctOptionIndexes={answer?.options}
|
208
163
|
options={options}
|
209
164
|
selectedOptions={multipleOptionAnswer}
|
@@ -214,14 +169,16 @@ export const QuestionCard = ({
|
|
214
169
|
isStopped={pollState === 'stopped'}
|
215
170
|
/>
|
216
171
|
) : null}
|
172
|
+
|
217
173
|
{isLive && (
|
218
174
|
<QuestionActions
|
219
175
|
isValidVote={isValidVote}
|
220
|
-
skippable={skippable}
|
221
|
-
onSkip={handleSkip}
|
222
176
|
onVote={handleVote}
|
223
177
|
response={localPeerResponse}
|
224
178
|
isQuiz={isQuiz}
|
179
|
+
incrementIndex={() => {
|
180
|
+
setCurrentIndex(curr => Math.min(totalQuestions, curr + 1));
|
181
|
+
}}
|
225
182
|
/>
|
226
183
|
)}
|
227
184
|
</Box>
|
@@ -229,15 +186,9 @@ export const QuestionCard = ({
|
|
229
186
|
);
|
230
187
|
};
|
231
188
|
|
232
|
-
const QuestionActions = ({ isValidVote,
|
189
|
+
const QuestionActions = ({ isValidVote, response, isQuiz, onVote, incrementIndex }) => {
|
233
190
|
return (
|
234
191
|
<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
192
|
{response ? (
|
242
193
|
<Text css={{ fontWeight: '$semiBold', color: '$on_surface_medium' }}>
|
243
194
|
{response.skipped ? 'Skipped' : null}
|
@@ -245,7 +196,14 @@ const QuestionActions = ({ isValidVote, skippable, response, isQuiz, onVote, onS
|
|
245
196
|
{!isQuiz && !response.skipped ? 'Voted' : null}
|
246
197
|
</Text>
|
247
198
|
) : (
|
248
|
-
<Button
|
199
|
+
<Button
|
200
|
+
css={{ p: '$xs $10', fontWeight: '$semiBold' }}
|
201
|
+
disabled={!isValidVote}
|
202
|
+
onClick={() => {
|
203
|
+
onVote();
|
204
|
+
incrementIndex();
|
205
|
+
}}
|
206
|
+
>
|
249
207
|
{isQuiz ? 'Answer' : 'Vote'}
|
250
208
|
</Button>
|
251
209
|
)}
|
@@ -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
|
-
//
|
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
|
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
|
-
{
|
78
|
+
{showSingleView ? <TimedView poll={poll} /> : <StandardView poll={poll} />}
|
78
79
|
|
79
|
-
{poll.state === 'started' &&
|
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={
|
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={
|
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}`}>
|
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>
|
@@ -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 = ({
|
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
|
};
|
@@ -314,7 +314,12 @@ export const TileMenuContent = ({
|
|
314
314
|
Volume ({volume})
|
315
315
|
</Box>
|
316
316
|
</Flex>
|
317
|
-
<Slider
|
317
|
+
<Slider
|
318
|
+
css={{ my: '0.5rem' }}
|
319
|
+
step={5}
|
320
|
+
value={[typeof volume === 'number' ? volume : 100]}
|
321
|
+
onValueChange={e => setVolume?.(e[0])}
|
322
|
+
/>
|
318
323
|
</StyledMenuTile.VolumeItem>
|
319
324
|
) : null}
|
320
325
|
|
@@ -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
|
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(
|
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) {
|
package/src/Sheet/Sheet.tsx
CHANGED