@100mslive/roomkit-react 0.2.2-alpha.2 → 0.2.2-alpha.4
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.
- 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