@selfcommunity/react-ui 0.11.0-alpha.23 → 0.11.0-alpha.25

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.
Files changed (22) hide show
  1. package/lib/cjs/components/LiveStreamForm/LiveStreamFormSettings.js +1 -1
  2. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/ControlBar.js +3 -1
  3. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/LiveStreamSettingsMenu.d.ts +8 -0
  4. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/LiveStreamSettingsMenu.js +69 -0
  5. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/PreJoin.d.ts +3 -2
  6. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/PreJoin.js +31 -2
  7. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/VideoConference.js +43 -4
  8. package/lib/cjs/constants/LiveStream.d.ts +1 -0
  9. package/lib/cjs/constants/LiveStream.js +2 -1
  10. package/lib/esm/components/LiveStreamForm/LiveStreamFormSettings.js +1 -1
  11. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/ControlBar.js +3 -1
  12. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/LiveStreamSettingsMenu.d.ts +8 -0
  13. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/LiveStreamSettingsMenu.js +66 -0
  14. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/PreJoin.d.ts +3 -2
  15. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/PreJoin.js +31 -2
  16. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/VideoConference.js +45 -6
  17. package/lib/esm/constants/LiveStream.d.ts +1 -0
  18. package/lib/esm/constants/LiveStream.js +1 -0
  19. package/lib/umd/{653.js → 212.js} +2 -2
  20. package/lib/umd/react-ui.js +1 -1
  21. package/package.json +6 -5
  22. /package/lib/umd/{653.js.LICENSE.txt → 212.js.LICENSE.txt} +0 -0
@@ -10,6 +10,10 @@ import ParticipantTileAvatar from './ParticipantTileAvatar';
10
10
  import { useEffect, useMemo } from 'react';
11
11
  import { TrackToggle } from './TrackToggle';
12
12
  import { useLiveStream } from './LiveStreamProvider';
13
+ import { BackgroundBlur } from '@livekit/track-processors';
14
+ import LiveStreamSettingsMenu from './LiveStreamSettingsMenu';
15
+ import { isClientSideRendering } from '@selfcommunity/utils';
16
+ import { CHOICE_VIDEO_BLUR_EFFECT } from '../../../constants/LiveStream';
13
17
  /** @alpha */
14
18
  export function usePreviewTracks(options, onError) {
15
19
  const [tracks, setTracks] = React.useState();
@@ -152,7 +156,8 @@ export function usePreviewDevice(enabled, deviceId, kind) {
152
156
  * @public
153
157
  */
154
158
  export function PreJoin(_a) {
155
- var { defaults = {}, onValidate, onSubmit, onError, debug, joinLabel = 'Join Room', micLabel = 'Microphone', camLabel = 'Camera', userLabel = 'Username', persistUserChoices = true } = _a, htmlProps = __rest(_a, ["defaults", "onValidate", "onSubmit", "onError", "debug", "joinLabel", "micLabel", "camLabel", "userLabel", "persistUserChoices"]);
159
+ var _b;
160
+ var { defaults = {}, onValidate, onSubmit, onError, debug, joinLabel = 'Join Room', micLabel = 'Microphone', camLabel = 'Camera', userLabel = 'Username', persistUserChoices = true, videoProcessor } = _a, htmlProps = __rest(_a, ["defaults", "onValidate", "onSubmit", "onError", "debug", "joinLabel", "micLabel", "camLabel", "userLabel", "persistUserChoices", "videoProcessor"]);
156
161
  const { liveStream } = useLiveStream();
157
162
  const scUserContext = useSCUser();
158
163
  const [userChoices, setUserChoices] = React.useState(defaultUserChoices);
@@ -171,6 +176,9 @@ export function PreJoin(_a) {
171
176
  const [audioDeviceId, setAudioDeviceId] = React.useState(initialUserChoices.audioDeviceId);
172
177
  const [videoDeviceId, setVideoDeviceId] = React.useState(initialUserChoices.videoDeviceId);
173
178
  const [username, setUsername] = React.useState(initialUserChoices.username);
179
+ // Processors
180
+ const [blurEnabled, setBlurEnabled] = React.useState(isClientSideRendering() ? Boolean((_b = window === null || window === void 0 ? void 0 : window.localStorage) === null || _b === void 0 ? void 0 : _b.getItem(CHOICE_VIDEO_BLUR_EFFECT)) || false : false);
181
+ const [processorPending, setProcessorPending] = React.useState(false);
174
182
  // Save user choices to persistent storage.
175
183
  React.useEffect(() => {
176
184
  saveAudioInputEnabled(audioEnabled && canUseAudio);
@@ -225,6 +233,11 @@ export function PreJoin(_a) {
225
233
  ((values.videoEnabled && values.videoDeviceId) || !values.videoEnabled));
226
234
  }
227
235
  }, [onValidate]);
236
+ const handleBlur = React.useCallback(() => {
237
+ var _a;
238
+ setBlurEnabled((enabled) => !enabled);
239
+ (_a = window === null || window === void 0 ? void 0 : window.localStorage) === null || _a === void 0 ? void 0 : _a.setItem(CHOICE_VIDEO_BLUR_EFFECT, (!blurEnabled).toString());
240
+ }, [setBlurEnabled, blurEnabled]);
228
241
  useEffect(() => {
229
242
  const newUserChoices = {
230
243
  username,
@@ -236,6 +249,22 @@ export function PreJoin(_a) {
236
249
  setUserChoices(newUserChoices);
237
250
  setIsValid(handleValidation(newUserChoices));
238
251
  }, [username, scUserContext.user, videoEnabled, handleValidation, audioEnabled, audioDeviceId, videoDeviceId]);
252
+ useEffect(() => {
253
+ if (videoTrack && videoEnabled) {
254
+ setProcessorPending(true);
255
+ try {
256
+ if (blurEnabled && !videoTrack.getProcessor()) {
257
+ videoTrack.setProcessor(BackgroundBlur(20));
258
+ }
259
+ else if (!blurEnabled) {
260
+ videoTrack.stopProcessor();
261
+ }
262
+ }
263
+ finally {
264
+ setProcessorPending(false);
265
+ }
266
+ }
267
+ }, [blurEnabled, videoTrack, videoEnabled]);
239
268
  function handleSubmit(event) {
240
269
  event.preventDefault();
241
270
  if (handleValidation(userChoices)) {
@@ -247,5 +276,5 @@ export function PreJoin(_a) {
247
276
  log.warn('Validation failed with: ', userChoices);
248
277
  }
249
278
  }
250
- return (_jsxs("div", Object.assign({ className: "lk-prejoin" }, htmlProps, { children: [_jsxs("div", Object.assign({ className: "lk-video-container" }, { children: [videoTrack && _jsx("video", { ref: videoEl, width: "1280", height: "720", "data-lk-facing-mode": facingMode }), (!videoTrack || !videoEnabled) && (_jsx("div", Object.assign({ className: "lk-camera-off-note" }, { children: _jsx(ParticipantTileAvatar, { user: scUserContext.user }) })))] })), _jsxs("div", Object.assign({ className: "lk-button-group-container" }, { children: [_jsxs("div", Object.assign({ className: "lk-button-group audio" }, { children: [_jsx(TrackToggle, Object.assign({ disabled: !canUseAudio, initialState: audioEnabled, source: Track.Source.Microphone, onChange: (enabled) => setAudioEnabled(enabled) }, { children: micLabel })), _jsx("div", Object.assign({ className: "lk-button-group-menu" }, { children: _jsx(MediaDeviceMenu, { initialSelection: audioDeviceId, kind: "audioinput", disabled: !audioTrack || !canUseAudio || !audioEnabled, tracks: { audioinput: audioTrack }, onActiveDeviceChange: (_, id) => setAudioDeviceId(id) }) }))] })), _jsxs("div", Object.assign({ className: "lk-button-group video" }, { children: [_jsx(TrackToggle, Object.assign({ disabled: !canUseVideo, initialState: videoEnabled, source: Track.Source.Camera, onChange: (enabled) => setVideoEnabled(enabled) }, { children: camLabel })), _jsx("div", Object.assign({ className: "lk-button-group-menu" }, { children: _jsx(MediaDeviceMenu, { initialSelection: videoDeviceId, kind: "videoinput", disabled: !videoTrack || !canUseVideo || !videoEnabled, tracks: { videoinput: videoTrack }, onActiveDeviceChange: (_, id) => setVideoDeviceId(id) }) }))] }))] })), _jsx("form", Object.assign({ className: "lk-username-container" }, { children: _jsx("button", Object.assign({ className: "lk-button lk-join-button", type: "submit", onClick: handleSubmit, disabled: !isValid || error }, { children: joinLabel })) })), debug && (_jsxs(_Fragment, { children: [_jsx("strong", { children: "User Choices:" }), _jsxs("ul", Object.assign({ className: "lk-list", style: { overflow: 'hidden', maxWidth: '15rem' } }, { children: [_jsxs("li", { children: ["Username: ", `${userChoices.username}`] }), _jsxs("li", { children: ["Video Enabled: ", `${userChoices.videoEnabled}`] }), _jsxs("li", { children: ["Audio Enabled: ", `${userChoices.audioEnabled}`] }), _jsxs("li", { children: ["Video Device: ", `${userChoices.videoDeviceId}`] }), _jsxs("li", { children: ["Audio Device: ", `${userChoices.audioDeviceId}`] })] }))] }))] })));
279
+ return (_jsxs("div", Object.assign({ className: "lk-prejoin" }, htmlProps, { children: [_jsxs("div", Object.assign({ className: "lk-video-container" }, { children: [videoTrack && _jsx("video", { ref: videoEl, width: "1280", height: "720", "data-lk-facing-mode": facingMode }), (!videoTrack || !videoEnabled) && (_jsx("div", Object.assign({ className: "lk-camera-off-note" }, { children: _jsx(ParticipantTileAvatar, { user: scUserContext.user }) })))] })), _jsxs("div", Object.assign({ className: "lk-button-group-container" }, { children: [_jsxs("div", Object.assign({ className: "lk-button-group audio" }, { children: [_jsx(TrackToggle, Object.assign({ disabled: !canUseAudio, initialState: audioEnabled, source: Track.Source.Microphone, onChange: (enabled) => setAudioEnabled(enabled) }, { children: micLabel })), _jsx("div", Object.assign({ className: "lk-button-group-menu" }, { children: _jsx(MediaDeviceMenu, { initialSelection: audioDeviceId, kind: "audioinput", disabled: !audioTrack || !canUseAudio || !audioEnabled, tracks: { audioinput: audioTrack }, onActiveDeviceChange: (_, id) => setAudioDeviceId(id) }) }))] })), _jsxs("div", Object.assign({ className: "lk-button-group video" }, { children: [_jsx(TrackToggle, Object.assign({ disabled: !canUseVideo, initialState: videoEnabled, source: Track.Source.Camera, onChange: (enabled) => setVideoEnabled(enabled) }, { children: camLabel })), _jsx("div", Object.assign({ className: "lk-button-group-menu" }, { children: _jsx(MediaDeviceMenu, { initialSelection: videoDeviceId, kind: "videoinput", disabled: !videoTrack || !canUseVideo || !videoEnabled, tracks: { videoinput: videoTrack }, onActiveDeviceChange: (_, id) => setVideoDeviceId(id) }) }))] })), _jsx(LiveStreamSettingsMenu, { actionBlurDisabled: !canUseVideo || !videoEnabled, blurEnabled: blurEnabled, handleBlur: handleBlur })] })), _jsx("form", Object.assign({ className: "lk-username-container" }, { children: _jsx("button", Object.assign({ className: "lk-button lk-join-button", type: "submit", onClick: handleSubmit, disabled: !isValid || error }, { children: joinLabel })) })), debug && (_jsxs(_Fragment, { children: [_jsx("strong", { children: "User Choices:" }), _jsxs("ul", Object.assign({ className: "lk-list", style: { overflow: 'hidden', maxWidth: '15rem' } }, { children: [_jsxs("li", { children: ["Username: ", `${userChoices.username}`] }), _jsxs("li", { children: ["Video Enabled: ", `${userChoices.videoEnabled}`] }), _jsxs("li", { children: ["Audio Enabled: ", `${userChoices.audioEnabled}`] }), _jsxs("li", { children: ["Video Device: ", `${userChoices.videoDeviceId}`] }), _jsxs("li", { children: ["Audio Device: ", `${userChoices.audioDeviceId}`] })] }))] }))] })));
251
280
  }
@@ -1,9 +1,9 @@
1
1
  import { __rest } from "tslib";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import * as React from 'react';
4
4
  import { isEqualTrackRef, isTrackReference, isWeb, log } from '@livekit/components-core';
5
5
  import { RoomEvent, Track } from 'livekit-client';
6
- import { CarouselLayout, Chat, ConnectionStateToast, GridLayout, LayoutContextProvider, RoomAudioRenderer, useCreateLayoutContext, useParticipants, usePinnedTracks, useTracks } from '@livekit/components-react';
6
+ import { CarouselLayout, Chat, ConnectionStateToast, GridLayout, LayoutContextProvider, RoomAudioRenderer, useCreateLayoutContext, useLocalParticipant, useParticipants, usePinnedTracks, useTracks } from '@livekit/components-react';
7
7
  import { ParticipantTile } from './ParticipantTile';
8
8
  import { ControlBar } from './ControlBar';
9
9
  import { useEffect, useMemo } from 'react';
@@ -15,6 +15,10 @@ import { styled } from '@mui/material/styles';
15
15
  import { Box } from '@mui/material';
16
16
  import { useThemeProps } from '@mui/system';
17
17
  import NoParticipants from './NoParticipants';
18
+ import LiveStreamSettingsMenu from './LiveStreamSettingsMenu';
19
+ import { BackgroundBlur } from '@livekit/track-processors';
20
+ import { isClientSideRendering } from '@selfcommunity/utils';
21
+ import { CHOICE_VIDEO_BLUR_EFFECT } from '../../../constants/LiveStream';
18
22
  const PREFIX = 'SCVideoConference';
19
23
  const classes = {
20
24
  root: `${PREFIX}-root`
@@ -35,7 +39,7 @@ const Root = styled(Box, {
35
39
  *
36
40
  */
37
41
  export function VideoConference(inProps) {
38
- var _a, _b;
42
+ var _a, _b, _c;
39
43
  // PROPS
40
44
  const props = useThemeProps({
41
45
  props: inProps,
@@ -52,16 +56,34 @@ export function VideoConference(inProps) {
52
56
  const lastAutoFocusedScreenShareTrack = React.useRef(null);
53
57
  // HOOKS
54
58
  const scUserContext = useSCUser();
59
+ const [blurEnabled, setBlurEnabled] = React.useState(isClientSideRendering() ? Boolean((_a = window === null || window === void 0 ? void 0 : window.localStorage) === null || _a === void 0 ? void 0 : _a.getItem(CHOICE_VIDEO_BLUR_EFFECT)) || false : false);
60
+ const [processorPending, setProcessorPending] = React.useState(false);
55
61
  const tracks = useTracks([
56
62
  { source: Track.Source.Camera, withPlaceholder: true },
57
63
  { source: Track.Source.ScreenShare, withPlaceholder: false }
58
64
  ], { updateOnlyOn: [RoomEvent.ActiveSpeakersChanged], onlySubscribed: false });
59
65
  const tracksNoParticipants = useMemo(() => tracks.filter((t) => t.participant.name === scUserContext.user.username || t.participant.name === speakerFocused.username || t.source === 'screen_share'), [tracks, scUserContext.user]);
66
+ const handleBlur = React.useCallback((event) => {
67
+ var _a, _b;
68
+ if (event.target) {
69
+ if ('checked' in event.target) {
70
+ setBlurEnabled((_a = event.target) === null || _a === void 0 ? void 0 : _a.checked);
71
+ }
72
+ else {
73
+ setBlurEnabled((enabled) => !enabled);
74
+ }
75
+ }
76
+ else {
77
+ setBlurEnabled((enabled) => !enabled);
78
+ }
79
+ (_b = window === null || window === void 0 ? void 0 : window.localStorage) === null || _b === void 0 ? void 0 : _b.setItem(CHOICE_VIDEO_BLUR_EFFECT, (!blurEnabled).toString());
80
+ }, [setBlurEnabled, blurEnabled]);
60
81
  const participants = useParticipants();
61
82
  const layoutContext = useCreateLayoutContext();
62
83
  const screenShareTracks = tracks.filter(isTrackReference).filter((track) => track.publication.source === Track.Source.ScreenShare);
63
- const focusTrack = (_a = usePinnedTracks(layoutContext)) === null || _a === void 0 ? void 0 : _a[0];
84
+ const focusTrack = (_b = usePinnedTracks(layoutContext)) === null || _b === void 0 ? void 0 : _b[0];
64
85
  const carouselTracks = tracks.filter((track) => !isEqualTrackRef(track, focusTrack));
86
+ const { cameraTrack } = useLocalParticipant();
65
87
  useLivestreamCheck();
66
88
  /**
67
89
  * widgetUpdate
@@ -117,7 +139,7 @@ export function VideoConference(inProps) {
117
139
  }
118
140
  }, [
119
141
  screenShareTracks.map((ref) => `${ref.publication.trackSid}_${ref.publication.isSubscribed}`).join(),
120
- (_b = focusTrack === null || focusTrack === void 0 ? void 0 : focusTrack.publication) === null || _b === void 0 ? void 0 : _b.trackSid,
142
+ (_c = focusTrack === null || focusTrack === void 0 ? void 0 : focusTrack.publication) === null || _c === void 0 ? void 0 : _c.trackSid,
121
143
  tracks,
122
144
  participants,
123
145
  speakerFocused
@@ -140,10 +162,27 @@ export function VideoConference(inProps) {
140
162
  }
141
163
  }
142
164
  }, [tracks, participants, speakerFocused]);
165
+ useEffect(() => {
166
+ const localCamTrack = cameraTrack === null || cameraTrack === void 0 ? void 0 : cameraTrack.track;
167
+ if (localCamTrack) {
168
+ setProcessorPending(true);
169
+ try {
170
+ if (blurEnabled && !localCamTrack.getProcessor()) {
171
+ localCamTrack.setProcessor(BackgroundBlur(20));
172
+ }
173
+ else if (!blurEnabled) {
174
+ localCamTrack.stopProcessor();
175
+ }
176
+ }
177
+ finally {
178
+ setProcessorPending(false);
179
+ }
180
+ }
181
+ }, [blurEnabled, cameraTrack]);
143
182
  return (_jsxs(Root, Object.assign({ className: classNames(className, classes.root, 'lk-video-conference') }, rest, { children: [isWeb() && (_jsxs(LayoutContextProvider, Object.assign({ value: layoutContext, onPinChange: handleFocusStateChange, onWidgetChange: widgetUpdate }, { children: [_jsxs("div", Object.assign({ className: "lk-video-conference-inner" }, { children: [!focusTrack ? (_jsx("div", Object.assign({ className: "lk-grid-layout-wrapper" }, { children: _jsx(GridLayout, Object.assign({ tracks: hideParticipantsList ? tracksNoParticipants : tracks }, { children: _jsx(ParticipantTile, {}) })) }))) : (_jsx("div", Object.assign({ className: "lk-focus-layout-wrapper" }, { children: hideParticipantsList ? (_jsx(FocusLayoutContainerNoParticipants, { children: focusTrack && _jsx(FocusLayout, { trackRef: focusTrack }) })) : (_jsxs(FocusLayoutContainer, { children: [carouselTracks.length ? (_jsx(CarouselLayout, Object.assign({ tracks: carouselTracks }, { children: _jsx(ParticipantTile, {}) }))) : (_jsx(NoParticipants, {})), focusTrack && _jsx(FocusLayout, { trackRef: focusTrack })] })) }))), _jsx(ControlBar, { controls: Object.assign({
144
183
  chat: !disableChat,
145
184
  microphone: !disableMicrophone,
146
185
  camera: !disableCamera,
147
186
  screenShare: !disableShareScreen
148
- }, { settings: !!SettingsComponent }) })] })), !disableChat && (_jsx(Chat, { style: { display: widgetState.showChat ? 'grid' : 'none' }, messageFormatter: chatMessageFormatter, messageEncoder: chatMessageEncoder, messageDecoder: chatMessageDecoder })), SettingsComponent && (_jsx("div", Object.assign({ className: "lk-settings-menu-modal", style: { display: widgetState.showSettings ? 'block' : 'none' } }, { children: _jsx(SettingsComponent, {}) })))] }))), _jsx(RoomAudioRenderer, {}), _jsx(ConnectionStateToast, {})] })));
187
+ }, { settings: true }) })] })), !disableChat && (_jsx(Chat, { style: { display: widgetState.showChat ? 'grid' : 'none' }, messageFormatter: chatMessageFormatter, messageEncoder: chatMessageEncoder, messageDecoder: chatMessageDecoder })), _jsx("div", Object.assign({ className: "lk-settings-menu-modal", style: { display: widgetState.showSettings ? 'block' : 'none' } }, { children: SettingsComponent ? (_jsx(SettingsComponent, {})) : (_jsx(_Fragment, { children: _jsx(LiveStreamSettingsMenu, { onlyContentMenu: true, actionBlurDisabled: !cameraTrack && !disableCamera, blurEnabled: blurEnabled, handleBlur: handleBlur }) })) }))] }))), _jsx(RoomAudioRenderer, {}), _jsx(ConnectionStateToast, {})] })));
149
188
  }
@@ -1,3 +1,4 @@
1
1
  export declare const LIVE_STREAM_TITLE_MAX_LENGTH = 100;
2
2
  export declare const LIVE_STREAM_SLUG_MAX_LENGTH = 50;
3
3
  export declare const LIVE_STREAM_DESCRIPTION_MAX_LENGTH = 500;
4
+ export declare const CHOICE_VIDEO_BLUR_EFFECT = "lk-video-blur";
@@ -1,3 +1,4 @@
1
1
  export const LIVE_STREAM_TITLE_MAX_LENGTH = 100;
2
2
  export const LIVE_STREAM_SLUG_MAX_LENGTH = 50;
3
3
  export const LIVE_STREAM_DESCRIPTION_MAX_LENGTH = 500;
4
+ export const CHOICE_VIDEO_BLUR_EFFECT = 'lk-video-blur';