@100mslive/roomkit-react 0.2.3 → 0.2.4-alpha.0
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/{HLSView-2BP4GO3Q.js → HLSView-5I6UAYPZ.js} +6 -6
- package/dist/HLSView-5I6UAYPZ.js.map +7 -0
- package/dist/Prebuilt/common/constants.d.ts +1 -0
- package/dist/Prebuilt/components/Connection/TileConnection.d.ts +2 -3
- package/dist/Prebuilt/components/InsetTile.d.ts +3 -1
- package/dist/Prebuilt/components/LayoutModeSelector.d.ts +2 -0
- package/dist/Prebuilt/components/MoreSettings/ChangeNameContent.d.ts +10 -0
- package/dist/Prebuilt/components/MoreSettings/ChangeNameModal.d.ts +5 -0
- package/dist/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.d.ts +2 -0
- package/dist/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.d.ts +1 -2
- package/dist/Prebuilt/components/Polls/common/StatusIndicator.d.ts +4 -3
- package/dist/Prebuilt/components/Polls/common/constants.d.ts +5 -0
- package/dist/Prebuilt/components/ScreenshareDisplay.d.ts +2 -0
- package/dist/Prebuilt/components/ScreenshareTile.d.ts +7 -0
- package/dist/Prebuilt/components/SecondaryTiles.d.ts +1 -1
- package/dist/Prebuilt/components/Settings/LayoutSettings.d.ts +11 -0
- package/dist/Prebuilt/components/Settings/NotificationSettings.d.ts +2 -0
- package/dist/Prebuilt/components/Settings/SwitchWithLabel.d.ts +10 -0
- package/dist/Prebuilt/components/Settings/common.d.ts +878 -0
- package/dist/Prebuilt/components/TileMenu/TileMenu.d.ts +14 -0
- package/dist/Prebuilt/components/TileMenu/TileMenuContent.d.ts +7 -7
- package/dist/Prebuilt/components/VideoLayouts/ProminenceLayout.d.ts +4 -2
- package/dist/Prebuilt/components/VideoLayouts/interface.d.ts +1 -0
- package/dist/Prebuilt/components/VideoTile.d.ts +19 -0
- package/dist/Prebuilt/components/hooks/useDropdownList.d.ts +4 -0
- package/dist/{chunk-G25T3EBJ.js → chunk-OGCNZHHH.js} +1328 -1097
- package/dist/chunk-OGCNZHHH.js.map +7 -0
- package/dist/index.cjs.js +1826 -1575
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +458 -293
- package/dist/meta.esbuild.json +470 -305
- package/package.json +6 -6
- package/src/Pagination/StyledPagination.tsx +1 -0
- package/src/Popover/index.tsx +8 -1
- package/src/Prebuilt/common/constants.ts +1 -0
- package/src/Prebuilt/components/AppData/AppData.tsx +2 -0
- package/src/Prebuilt/components/AppData/useUISettings.js +1 -1
- package/src/Prebuilt/components/Connection/TileConnection.tsx +13 -6
- package/src/Prebuilt/components/HlsStatsOverlay.jsx +2 -2
- package/src/Prebuilt/components/InsetTile.tsx +13 -6
- package/src/Prebuilt/components/LayoutModeSelector.tsx +106 -0
- package/src/Prebuilt/components/MoreSettings/{ChangeNameContent.jsx → ChangeNameContent.tsx} +10 -2
- package/src/Prebuilt/components/MoreSettings/{ChangeNameModal.jsx → ChangeNameModal.tsx} +14 -5
- package/src/Prebuilt/components/MoreSettings/SplitComponents/DesktopOptions.tsx +2 -2
- package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +2 -2
- package/src/Prebuilt/components/Notifications/Notifications.tsx +0 -1
- package/src/Prebuilt/components/Playlist/VideoPlayer.jsx +1 -1
- package/src/Prebuilt/components/Polls/CreatePollQuiz/{PollsQuizMenu.jsx → PollsQuizMenu.tsx} +54 -26
- package/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx +21 -31
- package/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.tsx +3 -17
- package/src/Prebuilt/components/Polls/Voting/LeaderboardSummary.tsx +1 -1
- package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +1 -10
- package/src/Prebuilt/components/Polls/Voting/Voting.tsx +1 -3
- package/src/Prebuilt/components/Polls/common/StatusIndicator.tsx +12 -3
- package/src/Prebuilt/components/Polls/common/constants.ts +5 -0
- package/src/Prebuilt/components/Preview/PreviewForm.tsx +2 -2
- package/src/Prebuilt/components/PreviousRoleInMetadata.tsx +1 -1
- package/src/Prebuilt/components/{ScreenshareTile.jsx → ScreenshareTile.tsx} +39 -6
- package/src/Prebuilt/components/SecondaryTiles.tsx +36 -4
- package/src/Prebuilt/components/Settings/DeviceSettings.jsx +1 -1
- package/src/Prebuilt/components/Settings/{LayoutSettings.jsx → LayoutSettings.tsx} +58 -14
- package/src/Prebuilt/components/Settings/{NotificationSettings.jsx → NotificationSettings.tsx} +14 -3
- package/src/Prebuilt/components/Settings/SettingsModal.jsx +32 -6
- package/src/Prebuilt/components/Settings/{SwitchWithLabel.jsx → SwitchWithLabel.tsx} +15 -1
- package/src/Prebuilt/components/Settings/common.ts +16 -0
- package/src/Prebuilt/components/TileMenu/{TileMenu.jsx → TileMenu.tsx} +12 -4
- package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +12 -10
- package/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx +29 -14
- package/src/Prebuilt/components/VideoLayouts/RoleProminence.tsx +12 -2
- package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +20 -5
- package/src/Prebuilt/components/VideoLayouts/interface.ts +1 -0
- package/src/Prebuilt/components/{VideoTile.jsx → VideoTile.tsx} +57 -44
- package/src/Prebuilt/components/VirtualBackground/VBPicker.tsx +2 -2
- package/src/Prebuilt/components/hooks/{useDropdownList.jsx → useDropdownList.ts} +2 -1
- package/src/Prebuilt/components/pdfAnnotator/shareScreenOptions.jsx +1 -1
- package/src/Prebuilt/layouts/HLSView.jsx +2 -2
- package/src/Prebuilt/layouts/SidePane.tsx +8 -4
- package/src/Prebuilt/layouts/VideoStreamingSection.tsx +1 -1
- package/src/VideoTile/StyledVideoTile.tsx +4 -4
- package/dist/HLSView-2BP4GO3Q.js.map +0 -7
- package/dist/chunk-G25T3EBJ.js.map +0 -7
- package/src/Prebuilt/components/Settings/common.js +0 -41
- /package/src/Prebuilt/components/{ScreenshareDisplay.jsx → ScreenshareDisplay.tsx} +0 -0
@@ -11,30 +11,26 @@ import { usePollViewToggle } from '../../AppData/useSidepane';
|
|
11
11
|
import { usePollViewState } from '../../AppData/useUISettings';
|
12
12
|
import { POLL_VIEWS } from '../../../common/constants';
|
13
13
|
|
14
|
+
const getEditableFormat = questions => {
|
15
|
+
const editableQuestions = questions.map(question => {
|
16
|
+
return { ...question, saved: true, draftID: uuid() };
|
17
|
+
});
|
18
|
+
return editableQuestions;
|
19
|
+
};
|
20
|
+
|
14
21
|
export function CreateQuestions() {
|
15
|
-
const [questions, setQuestions] = useState([{ draftID: uuid() }]);
|
16
22
|
const actions = useHMSActions();
|
17
23
|
const { isHLSRunning } = useRecordingStreaming();
|
18
24
|
const togglePollView = usePollViewToggle();
|
19
25
|
const { pollInView: id, setPollView } = usePollViewState();
|
20
26
|
const interaction = useHMSStore(selectPollByID(id));
|
21
|
-
|
22
|
-
|
23
|
-
() => questions.length > 0 && questions.every(question => isValidQuestion(question)),
|
24
|
-
[questions],
|
27
|
+
const [questions, setQuestions] = useState(
|
28
|
+
interaction.questions?.length ? getEditableFormat(interaction.questions) : [{ draftID: uuid() }],
|
25
29
|
);
|
26
30
|
|
31
|
+
const isValidPoll = useMemo(() => questions.length > 0 && questions.every(isValidQuestion), [questions]);
|
32
|
+
|
27
33
|
const launchPoll = async () => {
|
28
|
-
const validQuestions = questions
|
29
|
-
.filter(question => isValidQuestion(question))
|
30
|
-
.map(question => ({
|
31
|
-
text: question.text,
|
32
|
-
type: question.type,
|
33
|
-
options: question.options,
|
34
|
-
skippable: question.skippable,
|
35
|
-
weight: question.weight,
|
36
|
-
}));
|
37
|
-
await actions.interactivityCenter.addQuestionsToPoll(id, validQuestions);
|
38
34
|
await actions.interactivityCenter.startPoll(id);
|
39
35
|
await sendTimedMetadata(id);
|
40
36
|
setPollView(POLL_VIEWS.VOTE);
|
@@ -75,17 +71,17 @@ export function CreateQuestions() {
|
|
75
71
|
question={question}
|
76
72
|
index={index}
|
77
73
|
length={questions.length}
|
78
|
-
onSave={questionParams => {
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
74
|
+
onSave={async questionParams => {
|
75
|
+
const updatedQuestions = [...questions.slice(0, index), questionParams, ...questions.slice(index + 1)];
|
76
|
+
setQuestions(updatedQuestions);
|
77
|
+
const validQuestions = updatedQuestions.filter(question => isValidQuestion(question));
|
78
|
+
|
79
|
+
await actions.interactivityCenter.addQuestionsToPoll(id, validQuestions);
|
84
80
|
}}
|
85
81
|
isQuiz={isQuiz}
|
86
82
|
removeQuestion={questionID =>
|
87
83
|
setQuestions(prev => {
|
88
|
-
return prev.filter(questionFromSet => questionID !== questionFromSet
|
84
|
+
return prev.filter(questionFromSet => questionID !== questionFromSet?.draftID);
|
89
85
|
})
|
90
86
|
}
|
91
87
|
convertToDraft={questionID =>
|
@@ -117,8 +113,8 @@ export function CreateQuestions() {
|
|
117
113
|
</Text>
|
118
114
|
</Flex>
|
119
115
|
<Flex css={{ w: '100%' }} justify="end">
|
120
|
-
<Button disabled={!isValidPoll} onClick={launchPoll}>
|
121
|
-
Launch {interaction
|
116
|
+
<Button disabled={!isValidPoll} onClick={async () => launchPoll()}>
|
117
|
+
Launch {interaction?.type}
|
122
118
|
</Button>
|
123
119
|
</Flex>
|
124
120
|
</Flex>
|
@@ -130,13 +126,7 @@ const QuestionCard = ({ question, onSave, index, length, removeQuestion, isQuiz,
|
|
130
126
|
return (
|
131
127
|
<Flex direction="column" css={{ p: '$md', bg: '$surface_default', r: '$1', mb: '$sm' }}>
|
132
128
|
{question.saved ? (
|
133
|
-
<SavedQuestion
|
134
|
-
question={question}
|
135
|
-
index={index}
|
136
|
-
length={length}
|
137
|
-
convertToDraft={convertToDraft}
|
138
|
-
removeQuestion={removeQuestion}
|
139
|
-
/>
|
129
|
+
<SavedQuestion question={question} index={index} length={length} convertToDraft={convertToDraft} />
|
140
130
|
) : (
|
141
131
|
<QuestionForm
|
142
132
|
question={question}
|
@@ -1,10 +1,7 @@
|
|
1
|
-
import React
|
1
|
+
import React from 'react';
|
2
2
|
import { HMSPollQuestion } from '@100mslive/react-sdk';
|
3
|
-
import { CheckCircleIcon
|
3
|
+
import { CheckCircleIcon } from '@100mslive/react-icons';
|
4
4
|
import { Button, Flex, Text } from '../../../../';
|
5
|
-
// @ts-ignore
|
6
|
-
import IconButton from '../../../IconButton';
|
7
|
-
import { DeleteQuestionModal } from './DeleteQuestionModal';
|
8
5
|
import { QUESTION_TYPE_TITLE } from '../../../common/constants';
|
9
6
|
|
10
7
|
export const SavedQuestion = ({
|
@@ -12,15 +9,12 @@ export const SavedQuestion = ({
|
|
12
9
|
index,
|
13
10
|
length,
|
14
11
|
convertToDraft,
|
15
|
-
removeQuestion,
|
16
12
|
}: {
|
17
13
|
question: HMSPollQuestion & { draftID: number };
|
18
14
|
index: number;
|
19
15
|
length: number;
|
20
16
|
convertToDraft: (draftID: number) => void;
|
21
|
-
removeQuestion: (draftID: number) => void;
|
22
17
|
}) => {
|
23
|
-
const [openDeleteModal, setOpenDeleteModal] = useState(false);
|
24
18
|
return (
|
25
19
|
<>
|
26
20
|
<Text variant="overline" css={{ c: '$on_surface_low', textTransform: 'uppercase' }}>
|
@@ -48,19 +42,11 @@ export const SavedQuestion = ({
|
|
48
42
|
Not required to answer
|
49
43
|
</Text>
|
50
44
|
) : null}
|
51
|
-
<Flex justify="end" css={{ w: '100%', alignItems: 'center'
|
52
|
-
<IconButton onClick={() => setOpenDeleteModal(true)} css={{ background: 'none' }}>
|
53
|
-
<TrashIcon />
|
54
|
-
</IconButton>
|
45
|
+
<Flex justify="end" css={{ w: '100%', alignItems: 'center' }}>
|
55
46
|
<Button variant="standard" css={{ fontWeight: '$semiBold' }} onClick={() => convertToDraft(question.draftID)}>
|
56
47
|
Edit
|
57
48
|
</Button>
|
58
49
|
</Flex>
|
59
|
-
<DeleteQuestionModal
|
60
|
-
removeQuestion={() => removeQuestion(question.draftID)}
|
61
|
-
open={openDeleteModal}
|
62
|
-
setOpen={setOpenDeleteModal}
|
63
|
-
/>
|
64
50
|
</>
|
65
51
|
);
|
66
52
|
};
|
@@ -44,7 +44,7 @@ export const LeaderboardSummary = ({ pollID }: { pollID: string }) => {
|
|
44
44
|
<Text variant="lg" css={{ fontWeight: '$semiBold' }}>
|
45
45
|
{quiz.title}
|
46
46
|
</Text>
|
47
|
-
<StatusIndicator
|
47
|
+
<StatusIndicator status={quiz.state} />
|
48
48
|
</Flex>
|
49
49
|
<Flex
|
50
50
|
css={{ color: '$on_surface_medium', '&:hover': { color: '$on_surface_high', cursor: 'pointer' } }}
|
@@ -73,16 +73,7 @@ export const QuestionCard = ({
|
|
73
73
|
},
|
74
74
|
]);
|
75
75
|
startTime.current = Date.now();
|
76
|
-
}, [
|
77
|
-
isValidVote,
|
78
|
-
actions.interactivityCenter,
|
79
|
-
pollID,
|
80
|
-
index,
|
81
|
-
singleOptionAnswer,
|
82
|
-
multipleOptionAnswer,
|
83
|
-
totalQuestions,
|
84
|
-
isQuiz,
|
85
|
-
]);
|
76
|
+
}, [isValidVote, actions.interactivityCenter, pollID, index, singleOptionAnswer, multipleOptionAnswer]);
|
86
77
|
|
87
78
|
return (
|
88
79
|
<Box
|
@@ -33,8 +33,6 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
|
|
33
33
|
|
34
34
|
const canViewLeaderboard = poll.type === 'quiz' && poll.state === 'stopped' && !poll.anonymous;
|
35
35
|
|
36
|
-
const isLive = poll.state === 'started';
|
37
|
-
|
38
36
|
return (
|
39
37
|
<Container rounded>
|
40
38
|
<Flex
|
@@ -56,7 +54,7 @@ export const Voting = ({ id, toggleVoting }: { id: string; toggleVoting: () => v
|
|
56
54
|
<ChevronLeftIcon />
|
57
55
|
</Flex>
|
58
56
|
<Text variant="h6">{poll.title}</Text>
|
59
|
-
<StatusIndicator
|
57
|
+
<StatusIndicator status={poll.state} />
|
60
58
|
<Box
|
61
59
|
css={{
|
62
60
|
marginLeft: 'auto',
|
@@ -1,12 +1,21 @@
|
|
1
1
|
import React from 'react';
|
2
|
+
import { HMSPollState } from '@100mslive/react-sdk';
|
2
3
|
import { Flex, Text } from '../../../../';
|
4
|
+
import { PollStage } from './constants';
|
3
5
|
|
4
|
-
|
6
|
+
const statusMap: Record<HMSPollState, PollStage> = {
|
7
|
+
created: PollStage.DRAFT,
|
8
|
+
started: PollStage.LIVE,
|
9
|
+
stopped: PollStage.ENDED,
|
10
|
+
};
|
11
|
+
|
12
|
+
export const StatusIndicator = ({ status }: { status?: HMSPollState }) => {
|
13
|
+
if (!status) return null;
|
5
14
|
return (
|
6
15
|
<Flex align="center">
|
7
16
|
<Flex
|
8
17
|
css={{
|
9
|
-
backgroundColor:
|
18
|
+
backgroundColor: statusMap[status] === PollStage.LIVE ? '$alert_error_default' : '$secondary_default',
|
10
19
|
p: '$2 $4',
|
11
20
|
borderRadius: '$0',
|
12
21
|
}}
|
@@ -18,7 +27,7 @@ export const StatusIndicator = ({ isLive }: { isLive: boolean }) => {
|
|
18
27
|
color: '$on_primary_high',
|
19
28
|
}}
|
20
29
|
>
|
21
|
-
{
|
30
|
+
{statusMap[status]}
|
22
31
|
</Text>
|
23
32
|
</Flex>
|
24
33
|
</Flex>
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
2
2
|
import { useMedia } from 'react-use';
|
3
3
|
import { JoinForm_JoinBtnType } from '@100mslive/types-prebuilt/elements/join_form';
|
4
4
|
import { useRecordingStreaming } from '@100mslive/react-sdk';
|
5
|
-
import {
|
5
|
+
import { GoLiveIcon } from '@100mslive/react-icons';
|
6
6
|
import { Button, config as cssConfig, Flex, Input, styled } from '../../..';
|
7
7
|
import { useRoomLayout } from '../../provider/roomLayoutProvider';
|
8
8
|
// @ts-ignore: No implicit Any
|
@@ -72,7 +72,7 @@ const PreviewForm = ({
|
|
72
72
|
|
73
73
|
<Button type="submit" icon disabled={!name || !enableJoin} onClick={onJoin}>
|
74
74
|
{/* Conditions to show go live: The first broadcaster joins a streaming kit that is not live */}
|
75
|
-
{showGoLive ? <
|
75
|
+
{showGoLive ? <GoLiveIcon height={18} width={18} /> : null}
|
76
76
|
{showGoLive ? joinForm.go_live_btn_label : joinForm.join_btn_label}
|
77
77
|
</Button>
|
78
78
|
</Form>
|
@@ -10,7 +10,7 @@ export const PreviousRoleInMetadata = () => {
|
|
10
10
|
useEffect(() => {
|
11
11
|
let previousRole = vanillaStore.getState(selectLocalPeerRoleName);
|
12
12
|
const unsubscribe = vanillaStore.subscribe(currentRole => {
|
13
|
-
if (previousRole !== currentRole) {
|
13
|
+
if (previousRole !== currentRole && currentRole) {
|
14
14
|
updateMetaData({ prevRole: previousRole });
|
15
15
|
previousRole = currentRole;
|
16
16
|
}
|
@@ -10,11 +10,16 @@ import {
|
|
10
10
|
} from '@100mslive/react-sdk';
|
11
11
|
import { ExpandIcon, ShrinkIcon } from '@100mslive/react-icons';
|
12
12
|
import TileMenu from './TileMenu/TileMenu';
|
13
|
+
import { Box } from '../../Layout';
|
13
14
|
import { VideoTileStats } from '../../Stats';
|
15
|
+
import { useTheme } from '../../Theme';
|
14
16
|
import { Video } from '../../Video';
|
15
17
|
import { StyledVideoTile } from '../../VideoTile';
|
18
|
+
import { LayoutModeSelector } from './LayoutModeSelector';
|
19
|
+
// @ts-ignore: No implicit Any
|
16
20
|
import { getVideoTileLabel } from './peerTileUtils';
|
17
21
|
import { ScreenshareDisplay } from './ScreenshareDisplay';
|
22
|
+
// @ts-ignore: No implicit Any
|
18
23
|
import { useUISettings } from './AppData/useUISettings';
|
19
24
|
import { UI_SETTINGS } from '../common/constants';
|
20
25
|
|
@@ -27,14 +32,15 @@ const labelStyles = {
|
|
27
32
|
flexShrink: 0,
|
28
33
|
};
|
29
34
|
|
30
|
-
const Tile = ({ peerId, width = '100%', height = '100%' }) => {
|
35
|
+
const Tile = ({ peerId, width = '100%', height = '100%' }: { peerId: string; width?: string; height?: string }) => {
|
31
36
|
const isLocal = useHMSStore(selectLocalPeerID) === peerId;
|
32
37
|
const track = useHMSStore(selectScreenShareByPeerID(peerId));
|
38
|
+
const { theme } = useTheme();
|
33
39
|
const peer = useHMSStore(selectPeerByID(peerId));
|
34
40
|
const isAudioOnly = useUISettings(UI_SETTINGS.isAudioOnly);
|
35
41
|
const [isMouseHovered, setIsMouseHovered] = useState(false);
|
36
42
|
const showStatsOnTiles = useUISettings(UI_SETTINGS.showStatsOnTiles);
|
37
|
-
const fullscreenRef = useRef(null);
|
43
|
+
const fullscreenRef = useRef<HTMLDivElement | null>(null);
|
38
44
|
// fullscreen is for desired state
|
39
45
|
const [fullscreen, setFullscreen] = useState(false);
|
40
46
|
// isFullscreen is for true state
|
@@ -44,7 +50,7 @@ const Tile = ({ peerId, width = '100%', height = '100%' }) => {
|
|
44
50
|
const isFullScreenSupported = screenfull.isEnabled;
|
45
51
|
const audioTrack = useHMSStore(selectScreenShareAudioByPeerID(peer?.id));
|
46
52
|
|
47
|
-
if (isLocal && !['browser', 'window', 'application'].includes(track
|
53
|
+
if (isLocal && track?.displaySurface && !['browser', 'window', 'application'].includes(track.displaySurface)) {
|
48
54
|
return <ScreenshareDisplay />;
|
49
55
|
}
|
50
56
|
|
@@ -59,7 +65,15 @@ const Tile = ({ peerId, width = '100%', height = '100%' }) => {
|
|
59
65
|
});
|
60
66
|
|
61
67
|
return (
|
62
|
-
<StyledVideoTile.Root
|
68
|
+
<StyledVideoTile.Root
|
69
|
+
css={{
|
70
|
+
width,
|
71
|
+
height,
|
72
|
+
p: 0,
|
73
|
+
minHeight: 0,
|
74
|
+
}}
|
75
|
+
data-testid="screenshare_tile"
|
76
|
+
>
|
63
77
|
<StyledVideoTile.Container
|
64
78
|
transparentBg
|
65
79
|
ref={fullscreenRef}
|
@@ -73,14 +87,33 @@ const Tile = ({ peerId, width = '100%', height = '100%' }) => {
|
|
73
87
|
<VideoTileStats audioTrackID={audioTrack?.id} videoTrackID={track?.id} peerID={peerId} isLocal={isLocal} />
|
74
88
|
) : null}
|
75
89
|
{isFullScreenSupported && isMouseHovered ? (
|
76
|
-
<StyledVideoTile.FullScreenButton
|
90
|
+
<StyledVideoTile.FullScreenButton
|
91
|
+
css={{ bg: `${theme.colors.background_dim.value}A3` }}
|
92
|
+
onClick={() => setFullscreen(!fullscreen)}
|
93
|
+
>
|
77
94
|
{isFullscreen ? <ShrinkIcon /> : <ExpandIcon />}
|
78
95
|
</StyledVideoTile.FullScreenButton>
|
79
96
|
) : null}
|
97
|
+
{isMouseHovered && (
|
98
|
+
<Box
|
99
|
+
css={{
|
100
|
+
position: 'absolute',
|
101
|
+
top: '$2',
|
102
|
+
r: '$1',
|
103
|
+
h: '$14',
|
104
|
+
right: isFullScreenSupported ? '$17' : '$2',
|
105
|
+
zIndex: 5,
|
106
|
+
bg: `${theme.colors.background_dim.value}A3`,
|
107
|
+
}}
|
108
|
+
>
|
109
|
+
<LayoutModeSelector />
|
110
|
+
</Box>
|
111
|
+
)}
|
112
|
+
|
80
113
|
{track ? (
|
81
114
|
<Video
|
82
115
|
screenShare={true}
|
83
|
-
mirror={peer.isLocal
|
116
|
+
mirror={peer.isLocal}
|
84
117
|
attach={!isAudioOnly}
|
85
118
|
trackId={track.id}
|
86
119
|
css={{ minHeight: 0 }}
|
@@ -1,16 +1,39 @@
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
2
2
|
import { useMedia } from 'react-use';
|
3
|
+
import { selectAppData, selectSessionStore, selectTrackByID, useHMSStore } from '@100mslive/react-sdk';
|
3
4
|
import { LayoutProps } from './VideoLayouts/interface';
|
4
5
|
import { ProminenceLayout } from './VideoLayouts/ProminenceLayout';
|
5
6
|
import { config as cssConfig } from '../../Theme';
|
6
7
|
import { Pagination } from './Pagination';
|
7
8
|
import { usePagesWithTiles } from './hooks/useTileLayout';
|
9
|
+
import { APP_DATA, SESSION_STORE_KEY } from '../common/constants';
|
8
10
|
|
9
|
-
export const SecondaryTiles = ({ peers, onPageChange, onPageSize, edgeToEdge }: LayoutProps) => {
|
11
|
+
export const SecondaryTiles = ({ peers, onPageChange, onPageSize, edgeToEdge, hasSidebar }: LayoutProps) => {
|
10
12
|
const isMobile = useMedia(cssConfig.media.md);
|
11
13
|
const maxTileCount = isMobile ? 2 : 4;
|
12
|
-
const pagesWithTiles = usePagesWithTiles({ peers, maxTileCount });
|
13
14
|
const [page, setPage] = useState(0);
|
15
|
+
const pinnedTrackId = useHMSStore(selectAppData(APP_DATA.pinnedTrackId));
|
16
|
+
const spotlightPeerId = useHMSStore(selectSessionStore(SESSION_STORE_KEY.SPOTLIGHT));
|
17
|
+
const activeScreensharePeerId = useHMSStore(selectAppData(APP_DATA.activeScreensharePeerId));
|
18
|
+
const pinnedPeer = useHMSStore(selectTrackByID(pinnedTrackId))?.peerId;
|
19
|
+
const pageChangedAfterPinning = useRef(false);
|
20
|
+
const pagesWithTiles = usePagesWithTiles({
|
21
|
+
peers:
|
22
|
+
spotlightPeerId || pinnedPeer
|
23
|
+
? [...peers].sort((p1, p2) => {
|
24
|
+
const peerIdList = [activeScreensharePeerId, pinnedPeer, spotlightPeerId];
|
25
|
+
// put active screenshare peer, pinned peer, spotlight peer at first
|
26
|
+
if (peerIdList.includes(p1)) {
|
27
|
+
return -1;
|
28
|
+
}
|
29
|
+
if (peerIdList.includes(p2)) {
|
30
|
+
return 1;
|
31
|
+
}
|
32
|
+
return 0;
|
33
|
+
})
|
34
|
+
: peers,
|
35
|
+
maxTileCount,
|
36
|
+
});
|
14
37
|
const pageSize = pagesWithTiles[0]?.length || 0;
|
15
38
|
|
16
39
|
useEffect(() => {
|
@@ -19,8 +42,17 @@ export const SecondaryTiles = ({ peers, onPageChange, onPageSize, edgeToEdge }:
|
|
19
42
|
}
|
20
43
|
}, [pageSize, onPageSize]);
|
21
44
|
|
45
|
+
useEffect(() => {
|
46
|
+
if ((pinnedPeer || spotlightPeerId) && page !== 0 && !pageChangedAfterPinning.current) {
|
47
|
+
setPage(0);
|
48
|
+
pageChangedAfterPinning.current = true;
|
49
|
+
} else if (!pinnedPeer && !spotlightPeerId) {
|
50
|
+
pageChangedAfterPinning.current = false;
|
51
|
+
}
|
52
|
+
}, [pinnedPeer, spotlightPeerId, page]);
|
53
|
+
|
22
54
|
return (
|
23
|
-
<ProminenceLayout.SecondarySection tiles={pagesWithTiles[page]} edgeToEdge={edgeToEdge}>
|
55
|
+
<ProminenceLayout.SecondarySection tiles={pagesWithTiles[page]} edgeToEdge={edgeToEdge} hasSidebar={hasSidebar}>
|
24
56
|
{!edgeToEdge && (
|
25
57
|
<Pagination
|
26
58
|
page={page}
|
@@ -15,7 +15,7 @@ import { config as cssConfig } from '../../../Theme';
|
|
15
15
|
import { DialogDropdownTrigger } from '../../primitives/DropdownTrigger';
|
16
16
|
import { useUISettings } from '../AppData/useUISettings';
|
17
17
|
import { useDropdownSelection } from '../hooks/useDropdownSelection';
|
18
|
-
import { settingOverflow } from './common
|
18
|
+
import { settingOverflow } from './common';
|
19
19
|
import { UI_SETTINGS } from '../../common/constants';
|
20
20
|
|
21
21
|
/**
|
@@ -1,18 +1,34 @@
|
|
1
1
|
import React, { useCallback } from 'react';
|
2
2
|
import { selectIsLocalScreenShared, selectIsLocalVideoEnabled, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
|
3
|
-
import {
|
3
|
+
import { GalleryIcon, PersonRectangleIcon, SidebarIcon } from '@100mslive/react-icons';
|
4
|
+
import { Box, Flex, Slider, Text } from '../../..';
|
4
5
|
import SwitchWithLabel from './SwitchWithLabel';
|
6
|
+
// @ts-ignore: No implicit Any
|
5
7
|
import { useSetUiSettings } from '../AppData/useUISettings';
|
6
|
-
import { settingOverflow } from './common
|
8
|
+
import { settingOverflow } from './common';
|
7
9
|
import { UI_SETTINGS } from '../../common/constants';
|
8
10
|
|
11
|
+
export const LayoutMode = {
|
12
|
+
SIDEBAR: 'Sidebar',
|
13
|
+
GALLERY: 'Gallery',
|
14
|
+
SPOTLIGHT: 'Spotlight',
|
15
|
+
};
|
16
|
+
|
17
|
+
export type LayoutModeKeys = keyof typeof LayoutMode;
|
18
|
+
|
19
|
+
export const LayoutModeIconMapping = {
|
20
|
+
[LayoutMode.GALLERY]: <GalleryIcon />,
|
21
|
+
[LayoutMode.SIDEBAR]: <SidebarIcon />,
|
22
|
+
[LayoutMode.SPOTLIGHT]: <PersonRectangleIcon />,
|
23
|
+
};
|
24
|
+
|
9
25
|
export const LayoutSettings = () => {
|
10
26
|
const hmsActions = useHMSActions();
|
11
27
|
const isLocalVideoEnabled = useHMSStore(selectIsLocalVideoEnabled);
|
12
28
|
const isLocalScreenShared = useHMSStore(selectIsLocalScreenShared);
|
13
29
|
const [{ isAudioOnly, maxTileCount, mirrorLocalVideo }, setUISettings] = useSetUiSettings();
|
14
30
|
const toggleIsAudioOnly = useCallback(
|
15
|
-
async isAudioOnlyModeOn => {
|
31
|
+
async (isAudioOnlyModeOn?: boolean) => {
|
16
32
|
if (isAudioOnlyModeOn) {
|
17
33
|
// turn off video and screen share if user switches to audio only mode
|
18
34
|
isLocalVideoEnabled && (await hmsActions.setLocalVideoEnabled(false));
|
@@ -25,17 +41,34 @@ export const LayoutSettings = () => {
|
|
25
41
|
|
26
42
|
return (
|
27
43
|
<Box className={settingOverflow()}>
|
28
|
-
<
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
44
|
+
<Box>
|
45
|
+
{/* <Text variant="md" css={{ fontWeight: '$semiBold' }}>
|
46
|
+
Layout Modes
|
47
|
+
</Text>
|
48
|
+
<RadioGroup.Root
|
49
|
+
css={{ flexDirection: 'column', alignItems: 'start', gap: '$10', my: '$2', py: '$8' }}
|
50
|
+
value={layoutMode}
|
51
|
+
onValueChange={value =>
|
52
|
+
setUISettings({
|
53
|
+
[UI_SETTINGS.layoutMode]: value,
|
54
|
+
})
|
55
|
+
}
|
56
|
+
>
|
57
|
+
{Object.keys(LayoutMode).map(key => {
|
58
|
+
return (
|
59
|
+
<Flex align="center" key={key} css={{ mr: '$8', gap: '$8' }}>
|
60
|
+
<RadioGroup.Item value={LayoutMode[key as LayoutModeKeys]} id={`layoutMode-${key}`} css={{ mr: '$4' }}>
|
61
|
+
<RadioGroup.Indicator />
|
62
|
+
</RadioGroup.Item>
|
63
|
+
<Label htmlFor={`layoutMode-${key}`} css={{ display: 'flex', gap: '$8', cursor: 'pointer' }}>
|
64
|
+
{LayoutModeIconMapping[LayoutMode[key as LayoutModeKeys]]}
|
65
|
+
{LayoutMode[key as LayoutModeKeys]}
|
66
|
+
</Label>
|
67
|
+
</Flex>
|
68
|
+
);
|
69
|
+
})}
|
70
|
+
</RadioGroup.Root> */}
|
71
|
+
</Box>
|
39
72
|
<Flex align="center" css={{ w: '100%', my: '$2', py: '$8', '@md': { display: 'none' } }}>
|
40
73
|
<Text variant="md" css={{ fontWeight: '$semiBold' }}>
|
41
74
|
Tiles In View({maxTileCount})
|
@@ -53,6 +86,17 @@ export const LayoutSettings = () => {
|
|
53
86
|
/>
|
54
87
|
</Flex>
|
55
88
|
</Flex>
|
89
|
+
<SwitchWithLabel label="Audio Only Mode" id="audioOnlyMode" checked={isAudioOnly} onChange={toggleIsAudioOnly} />
|
90
|
+
<SwitchWithLabel
|
91
|
+
label="Mirror Local Video"
|
92
|
+
id="mirrorMode"
|
93
|
+
checked={mirrorLocalVideo}
|
94
|
+
onChange={value => {
|
95
|
+
setUISettings({
|
96
|
+
[UI_SETTINGS.mirrorLocalVideo]: value,
|
97
|
+
});
|
98
|
+
}}
|
99
|
+
/>
|
56
100
|
</Box>
|
57
101
|
);
|
58
102
|
};
|
package/src/Prebuilt/components/Settings/{NotificationSettings.jsx → NotificationSettings.tsx}
RENAMED
@@ -1,12 +1,23 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { AlertOctagonIcon, HandIcon, PeopleAddIcon, PeopleRemoveIcon } from '@100mslive/react-icons';
|
3
|
-
import { Box } from '
|
3
|
+
import { Box } from '../../..';
|
4
4
|
import SwitchWithLabel from './SwitchWithLabel';
|
5
|
+
// @ts-ignore: No implicit Any
|
5
6
|
import { useSetSubscribedNotifications, useSubscribedNotifications } from '../AppData/useUISettings';
|
6
|
-
import { settingOverflow } from './common
|
7
|
+
import { settingOverflow } from './common';
|
7
8
|
import { SUBSCRIBED_NOTIFICATIONS } from '../../common/constants';
|
8
9
|
|
9
|
-
const NotificationItem = ({
|
10
|
+
const NotificationItem = ({
|
11
|
+
type,
|
12
|
+
label,
|
13
|
+
icon,
|
14
|
+
checked,
|
15
|
+
}: {
|
16
|
+
type: string;
|
17
|
+
label: string;
|
18
|
+
icon: React.ReactNode;
|
19
|
+
checked: boolean;
|
20
|
+
}) => {
|
10
21
|
const [, setSubscribedNotifications] = useSetSubscribedNotifications(type);
|
11
22
|
return (
|
12
23
|
<SwitchWithLabel
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import React, { useCallback, useEffect, useState } from 'react';
|
2
2
|
import { useMedia } from 'react-use';
|
3
|
-
import { ChevronLeftIcon, CrossIcon } from '@100mslive/react-icons';
|
3
|
+
import { ChevronLeftIcon, CrossIcon, GridFourIcon, NotificationsIcon, SettingsIcon } from '@100mslive/react-icons';
|
4
4
|
import { HorizontalDivider } from '../../../Divider';
|
5
5
|
import { IconButton } from '../../../IconButton';
|
6
6
|
import { Box, Flex } from '../../../Layout';
|
@@ -9,7 +9,31 @@ import { Sheet } from '../../../Sheet';
|
|
9
9
|
import { Tabs } from '../../../Tabs';
|
10
10
|
import { Text } from '../../../Text';
|
11
11
|
import { config as cssConfig } from '../../../Theme';
|
12
|
-
import
|
12
|
+
import DeviceSettings from './DeviceSettings';
|
13
|
+
import { LayoutSettings } from './LayoutSettings';
|
14
|
+
import { NotificationSettings } from './NotificationSettings';
|
15
|
+
import { settingContent } from './common';
|
16
|
+
|
17
|
+
const settingsList = [
|
18
|
+
{
|
19
|
+
tabName: 'devices',
|
20
|
+
title: 'Device Settings',
|
21
|
+
icon: SettingsIcon,
|
22
|
+
content: DeviceSettings,
|
23
|
+
},
|
24
|
+
{
|
25
|
+
tabName: 'notifications',
|
26
|
+
title: 'Notifications',
|
27
|
+
icon: NotificationsIcon,
|
28
|
+
content: NotificationSettings,
|
29
|
+
},
|
30
|
+
{
|
31
|
+
tabName: 'layout',
|
32
|
+
title: 'Layout',
|
33
|
+
icon: GridFourIcon,
|
34
|
+
content: LayoutSettings,
|
35
|
+
},
|
36
|
+
];
|
13
37
|
|
14
38
|
const SettingsModal = ({ open, onOpenChange, screenType, children = <></> }) => {
|
15
39
|
const mediaQueryLg = cssConfig.media.md;
|
@@ -53,8 +77,9 @@ const SettingsModal = ({ open, onOpenChange, screenType, children = <></> }) =>
|
|
53
77
|
showSetting={showSetting}
|
54
78
|
hideSettingByTabName={hideSettingByTabName}
|
55
79
|
resetSelection={resetSelection}
|
56
|
-
|
57
|
-
|
80
|
+
>
|
81
|
+
{children}
|
82
|
+
</MobileSettingModal>
|
58
83
|
) : (
|
59
84
|
<DesktopSettingModal
|
60
85
|
open={open}
|
@@ -64,8 +89,9 @@ const SettingsModal = ({ open, onOpenChange, screenType, children = <></> }) =>
|
|
64
89
|
showSetting={showSetting}
|
65
90
|
hideSettingByTabName={hideSettingByTabName}
|
66
91
|
resetSelection={resetSelection}
|
67
|
-
|
68
|
-
|
92
|
+
>
|
93
|
+
{children}
|
94
|
+
</DesktopSettingModal>
|
69
95
|
);
|
70
96
|
};
|
71
97
|
|
@@ -3,7 +3,21 @@ import { Label } from '../../../Label';
|
|
3
3
|
import { Flex } from '../../../Layout';
|
4
4
|
import { Switch } from '../../../Switch';
|
5
5
|
|
6
|
-
const SwitchWithLabel = ({
|
6
|
+
const SwitchWithLabel = ({
|
7
|
+
label,
|
8
|
+
icon,
|
9
|
+
id,
|
10
|
+
onChange,
|
11
|
+
checked,
|
12
|
+
hide = false,
|
13
|
+
}: {
|
14
|
+
label: string;
|
15
|
+
icon?: React.ReactNode;
|
16
|
+
id: string;
|
17
|
+
onChange: (value: boolean) => void;
|
18
|
+
checked: boolean;
|
19
|
+
hide?: boolean;
|
20
|
+
}) => {
|
7
21
|
return (
|
8
22
|
<Flex
|
9
23
|
align="center"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { css } from '../../..';
|
2
|
+
|
3
|
+
export const settingOverflow = css({
|
4
|
+
flex: '1 1 0',
|
5
|
+
pr: '$12',
|
6
|
+
mr: '-$12',
|
7
|
+
overflowY: 'auto',
|
8
|
+
});
|
9
|
+
|
10
|
+
export const settingContent = css({
|
11
|
+
display: 'flex',
|
12
|
+
flexDirection: 'column',
|
13
|
+
'&[hidden]': {
|
14
|
+
display: 'none',
|
15
|
+
},
|
16
|
+
});
|