@selfcommunity/react-ui 0.8.0-live.58 → 0.8.0-live.59

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 (56) hide show
  1. package/lib/cjs/components/CreateLiveStreamDialog/CreateLiveStreamDialog.js +8 -7
  2. package/lib/cjs/components/EventForm/EventAddress.js +7 -6
  3. package/lib/cjs/components/EventForm/EventForm.d.ts +6 -1
  4. package/lib/cjs/components/EventForm/EventForm.js +27 -9
  5. package/lib/cjs/components/EventForm/types.d.ts +2 -1
  6. package/lib/cjs/components/LiveStreamForm/LiveStreamFormSettings.d.ts +2 -1
  7. package/lib/cjs/components/LiveStreamForm/LiveStreamFormSettings.js +1 -1
  8. package/lib/cjs/components/LiveStreamForm/constants.d.ts +3 -0
  9. package/lib/cjs/components/LiveStreamForm/constants.js +6 -3
  10. package/lib/cjs/components/LiveStreamRoom/LiveStreamRoom.js +12 -6
  11. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/LiveStreamVideoConference.js +5 -1
  12. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantPlaceholder.d.ts +6 -0
  13. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantPlaceholder.js +8 -0
  14. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTile.d.ts +27 -0
  15. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTile.js +65 -0
  16. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTileActions.d.ts +26 -0
  17. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTileActions.js +194 -0
  18. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTileAvatar.d.ts +15 -0
  19. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTileAvatar.js +38 -0
  20. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/PreJoin.d.ts +61 -0
  21. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/PreJoin.js +247 -0
  22. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/TrackToggle.d.ts +23 -0
  23. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/TrackToggle.js +31 -0
  24. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/VideoConference.d.ts +4 -3
  25. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/VideoConference.js +5 -4
  26. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/constants.d.ts +1 -0
  27. package/lib/cjs/components/LiveStreamRoom/LiveStreamVideoConference/constants.js +2 -1
  28. package/lib/esm/components/CreateLiveStreamDialog/CreateLiveStreamDialog.js +2 -1
  29. package/lib/esm/components/EventForm/EventAddress.js +7 -6
  30. package/lib/esm/components/EventForm/EventForm.d.ts +6 -1
  31. package/lib/esm/components/EventForm/EventForm.js +27 -9
  32. package/lib/esm/components/EventForm/types.d.ts +2 -1
  33. package/lib/esm/components/LiveStreamForm/LiveStreamFormSettings.d.ts +2 -1
  34. package/lib/esm/components/LiveStreamForm/LiveStreamFormSettings.js +1 -1
  35. package/lib/esm/components/LiveStreamForm/constants.d.ts +3 -0
  36. package/lib/esm/components/LiveStreamForm/constants.js +6 -3
  37. package/lib/esm/components/LiveStreamRoom/LiveStreamRoom.js +13 -7
  38. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/LiveStreamVideoConference.js +5 -1
  39. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantPlaceholder.d.ts +6 -0
  40. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantPlaceholder.js +6 -0
  41. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTile.d.ts +27 -0
  42. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTile.js +60 -0
  43. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTileActions.d.ts +26 -0
  44. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTileActions.js +191 -0
  45. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTileAvatar.d.ts +15 -0
  46. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/ParticipantTileAvatar.js +35 -0
  47. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/PreJoin.d.ts +61 -0
  48. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/PreJoin.js +241 -0
  49. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/TrackToggle.d.ts +23 -0
  50. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/TrackToggle.js +27 -0
  51. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/VideoConference.d.ts +4 -3
  52. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/VideoConference.js +6 -5
  53. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/constants.d.ts +1 -0
  54. package/lib/esm/components/LiveStreamRoom/LiveStreamVideoConference/constants.js +1 -0
  55. package/lib/umd/react-ui.js +1 -1
  56. package/package.json +7 -7
@@ -0,0 +1,191 @@
1
+ import { __rest } from "tslib";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useContext, useMemo, useRef, useState } from 'react';
4
+ import { styled } from '@mui/material/styles';
5
+ import { FormattedMessage, useIntl } from 'react-intl';
6
+ import Popper from '@mui/material/Popper';
7
+ import Icon from '@mui/material/Icon';
8
+ import { useSnackbar } from 'notistack';
9
+ import classNames from 'classnames';
10
+ import { Box, ClickAwayListener, Grow, IconButton, ListItemIcon, ListItemText, MenuItem, MenuList, Paper, SwipeableDrawer, useMediaQuery, useTheme } from '@mui/material';
11
+ import { Endpoints, http } from '@selfcommunity/api-services';
12
+ import { SCContext, SCUserContext, UserUtils, useSCRouting } from '@selfcommunity/react-core';
13
+ import { BAN_ROOM_USER } from './constants';
14
+ import ConfirmDialog from '../../../shared/ConfirmDialog/ConfirmDialog';
15
+ import { useEnsureParticipant } from '@livekit/components-react';
16
+ const PREFIX = 'SCParticipantTileActionsMenu';
17
+ const classes = {
18
+ root: `${PREFIX}-root`,
19
+ button: `${PREFIX}-button`,
20
+ popperRoot: `${PREFIX}-popper-root`,
21
+ paper: `${PREFIX}-paper`,
22
+ item: `${PREFIX}-item`,
23
+ itemText: `${PREFIX}-item-text`,
24
+ subItem: `${PREFIX}-sub-item`,
25
+ subItemText: `${PREFIX}-sub-item-text`,
26
+ footerSubItems: `${PREFIX}-footer-sub-items`,
27
+ selectedIcon: `${PREFIX}-selected-icon`,
28
+ sectionBadge: `${PREFIX}-section-badge`,
29
+ sectionWithSelectionIcon: `${PREFIX}-section-with-selection-icon`,
30
+ visibilityIcons: `${PREFIX}-visibility-icons`
31
+ };
32
+ const PopperRoot = styled(Popper, {
33
+ name: PREFIX,
34
+ slot: 'Root',
35
+ overridesResolver: (props, styles) => styles.popperRoot
36
+ })(() => ({
37
+ '& .SCParticipantTileActionsMenu-paper': {
38
+ borderRadius: 5
39
+ }
40
+ }));
41
+ const Root = styled(Box, {
42
+ name: PREFIX,
43
+ slot: 'Root',
44
+ overridesResolver: (props, styles) => styles.root
45
+ })(({ theme }) => ({
46
+ display: 'inline-block',
47
+ '& button': {
48
+ color: theme.palette.common.white
49
+ }
50
+ }));
51
+ export default function ContributionActionsMenu(props) {
52
+ // PROPS
53
+ const { className, participant, onBanParticipant, PopperProps = {} } = props, rest = __rest(props, ["className", "participant", "onBanParticipant", "PopperProps"]);
54
+ // INTL
55
+ const intl = useIntl();
56
+ // CONTEXT
57
+ const theme = useTheme();
58
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
59
+ const scContext = useContext(SCContext);
60
+ const scUserContext = useContext(SCUserContext);
61
+ const scUserId = scUserContext.user ? scUserContext.user.id : null;
62
+ const scRoutingContext = useSCRouting();
63
+ const { enqueueSnackbar } = useSnackbar();
64
+ const p = useEnsureParticipant(participant);
65
+ // GENERAL POPPER STATE
66
+ const [open, setOpen] = useState(false);
67
+ const [isLoading, setIsLoading] = useState(false);
68
+ // CONFIRM ACTION DIALOG STATE
69
+ const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
70
+ const [currentAction, setCurrentAction] = useState(null);
71
+ const [currentActionLoading, setCurrentActionLoading] = useState(null);
72
+ // CONST
73
+ let popperRef = useRef(null);
74
+ /**
75
+ * Handles open popup
76
+ */
77
+ function handleOpen() {
78
+ if (scUserContext.user) {
79
+ setOpen(true);
80
+ }
81
+ else {
82
+ scContext.settings.handleAnonymousAction();
83
+ }
84
+ }
85
+ /**
86
+ * Closes popup
87
+ */
88
+ function handleClose() {
89
+ if (popperRef.current && popperRef.current.contains(event.target)) {
90
+ return;
91
+ }
92
+ setOpen(false);
93
+ if (rest.onClose) {
94
+ rest.onClose();
95
+ }
96
+ }
97
+ /**
98
+ * Perform delete contribution
99
+ */
100
+ const performBanParticipant = useMemo(() => () => {
101
+ return http
102
+ .request({
103
+ url: Endpoints.DeleteComment.url({ id: p.identity }),
104
+ method: Endpoints.DeleteComment.method
105
+ })
106
+ .then((res) => {
107
+ if (res.status >= 300) {
108
+ return Promise.reject(res);
109
+ }
110
+ return Promise.resolve(res.data);
111
+ });
112
+ }, [p]);
113
+ /**
114
+ * handle action
115
+ */
116
+ function handleAction(action) {
117
+ if (action === BAN_ROOM_USER) {
118
+ setCurrentAction(BAN_ROOM_USER);
119
+ setOpenConfirmDialog(true);
120
+ handleClose();
121
+ }
122
+ }
123
+ /**
124
+ * Perform additional operations at the end of single action
125
+ */
126
+ function performPostConfirmAction(success) {
127
+ if (success) {
128
+ setCurrentActionLoading(null);
129
+ setCurrentAction(null);
130
+ setOpenConfirmDialog(false);
131
+ enqueueSnackbar(_jsx(FormattedMessage, { id: "ui.contributionActionMenu.actionSuccess", defaultMessage: "ui.contributionActionMenu.actionSuccess" }), {
132
+ variant: 'success',
133
+ autoHideDuration: 3000
134
+ });
135
+ }
136
+ else {
137
+ setCurrentActionLoading(null);
138
+ enqueueSnackbar(_jsx(FormattedMessage, { id: "ui.contributionActionMenu.actionError", defaultMessage: "ui.contributionActionMenu.actionError" }), {
139
+ variant: 'error',
140
+ autoHideDuration: 3000
141
+ });
142
+ }
143
+ }
144
+ /**
145
+ * Delete a contribution
146
+ */
147
+ function handleConfirmedAction() {
148
+ if (p && !isLoading && !currentActionLoading) {
149
+ if (currentAction === BAN_ROOM_USER) {
150
+ setCurrentActionLoading(BAN_ROOM_USER);
151
+ /* performBanParticipant()
152
+ .then(() => {
153
+ onBanParticipant && onBanParticipant(p);
154
+ performPostConfirmAction(true);
155
+ })
156
+ .catch((error) => {
157
+ Logger.error(SCOPE_SC_UI, error);
158
+ performPostConfirmAction(false);
159
+ }); */
160
+ }
161
+ }
162
+ }
163
+ /**
164
+ * Can authenticated ban a user in a room
165
+ */
166
+ function canBanUser() {
167
+ return scUserContext.user && UserUtils.isStaff(scUserContext.user) && scUserContext.user.id.toString() !== p.identity;
168
+ }
169
+ /**
170
+ * Renders section general
171
+ */
172
+ function renderGeneralSection() {
173
+ return (_jsx(Box, { children: canBanUser() && (_jsxs(MenuItem, Object.assign({ className: classes.subItem, disabled: currentActionLoading === BAN_ROOM_USER }, { children: [_jsx(ListItemIcon, { children: _jsx(Icon, { children: "person" }) }), _jsx(ListItemText, { primary: _jsx(FormattedMessage, { id: "ui.liveStreamRoom.participantTileActions.banRoomUser", defaultMessage: "ui.liveStreamRoom.participantTileActions.banRoomUser" }), onClick: () => handleAction(BAN_ROOM_USER), classes: { root: classes.itemText } })] }))) }));
174
+ }
175
+ /**
176
+ * Renders contribution menu content
177
+ */
178
+ function renderContent() {
179
+ return (_jsx(Box, { children: _jsx(MenuList, { children: renderGeneralSection() }) }));
180
+ }
181
+ /**
182
+ * Renders component
183
+ */
184
+ return (_jsxs(Root, Object.assign({ className: classNames(classes.root, className) }, { children: [_jsx(IconButton, Object.assign({ ref: (ref) => {
185
+ popperRef.current = ref;
186
+ }, "aria-haspopup": "true", onClick: handleOpen, className: classes.button, size: "small" }, { children: _jsx(Icon, { children: "expand_more" }) })), isMobile ? (_jsx(SwipeableDrawer, Object.assign({ open: open, onClose: handleClose, onOpen: handleOpen, anchor: "bottom", disableSwipeToOpen: true }, { children: renderContent() }))) : (_jsx(PopperRoot, Object.assign({ open: open, anchorEl: popperRef.current, role: undefined, transition: true, className: classes.popperRoot }, PopperProps, { placement: "right" }, { children: ({ TransitionProps, placement }) => (_jsx(Grow, Object.assign({}, TransitionProps, { style: { transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' } }, { children: _jsx(Paper, Object.assign({ variant: 'outlined', className: classes.paper }, { children: _jsx(ClickAwayListener, Object.assign({ onClickAway: handleClose }, { children: renderContent() })) })) }))) }))), openConfirmDialog && (_jsx(ConfirmDialog, Object.assign({ open: openConfirmDialog }, (currentAction === BAN_ROOM_USER
187
+ ? {
188
+ content: (_jsx(FormattedMessage, { id: "ui.liveStreamRoom.participantTileActions.banRoomUser", defaultMessage: "ui.liveStreamRoom.participantTileActions.banRoomUser" }))
189
+ }
190
+ : {}), { onConfirm: handleConfirmedAction, isUpdating: Boolean(currentActionLoading), onClose: () => setOpenConfirmDialog(false) })))] })));
191
+ }
@@ -0,0 +1,15 @@
1
+ import { SCUserType } from '@selfcommunity/types';
2
+ export interface ParticipantTileProps {
3
+ className?: string;
4
+ /**
5
+ * User Object
6
+ * @default null
7
+ */
8
+ user?: SCUserType;
9
+ /**
10
+ * User Object
11
+ * @default null
12
+ */
13
+ participant?: any;
14
+ }
15
+ export default function UserAvatar(inProps: ParticipantTileProps): JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { __rest } from "tslib";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { styled } from '@mui/material/styles';
4
+ import { useThemeProps } from '@mui/system';
5
+ import { Box } from '@mui/material';
6
+ import classNames from 'classnames';
7
+ import ParticipantPlaceholder from './ParticipantPlaceholder';
8
+ import { useSCContext } from '@selfcommunity/react-core';
9
+ const PREFIX = 'SCParticipantTileAvatar';
10
+ const classes = {
11
+ root: `${PREFIX}-root`
12
+ };
13
+ const Root = styled(Box, {
14
+ name: PREFIX,
15
+ slot: 'Root',
16
+ overridesResolver: (props, styles) => styles.root
17
+ })(({ theme }) => ({
18
+ height: 'auto !important',
19
+ '& img': {
20
+ borderRadius: '50%',
21
+ width: 100,
22
+ height: 100
23
+ }
24
+ }));
25
+ export default function UserAvatar(inProps) {
26
+ // PROPS
27
+ const props = useThemeProps({
28
+ props: inProps,
29
+ name: PREFIX
30
+ });
31
+ const { className, user, participant } = props, rest = __rest(props, ["className", "user", "participant"]);
32
+ // CONTEXT
33
+ const scContext = useSCContext();
34
+ return (_jsx(Root, Object.assign({ className: classNames(className, classes.root) }, rest, { children: user ? (_jsx("img", { src: `${user.avatar}` })) : participant ? (_jsx("img", { src: `${scContext.settings.portal}/api/v2/avatar/${participant.identity}` })) : (_jsx(ParticipantPlaceholder, {})) })));
35
+ }
@@ -0,0 +1,61 @@
1
+ import type { CreateLocalTracksOptions, LocalAudioTrack, LocalTrack, LocalVideoTrack } from 'livekit-client';
2
+ import { Track } from 'livekit-client';
3
+ import * as React from 'react';
4
+ import type { LocalUserChoices } from '@livekit/components-core';
5
+ import { SCLiveStreamType } from '@selfcommunity/types';
6
+ /**
7
+ * Props for the PreJoin component.
8
+ * @public
9
+ */
10
+ export interface PreJoinProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSubmit' | 'onError'> {
11
+ /** This function is called with the `LocalUserChoices` if validation is passed. */
12
+ onSubmit?: (values: LocalUserChoices) => void;
13
+ /**
14
+ * Provide your custom validation function. Only if validation is successful the user choices are past to the onSubmit callback.
15
+ */
16
+ onValidate?: (values: LocalUserChoices) => boolean;
17
+ onError?: (error: Error) => void;
18
+ /**
19
+ * Livestream Object
20
+ * @default null
21
+ */
22
+ liveStream?: SCLiveStreamType;
23
+ /** Prefill the input form with initial values. */
24
+ defaults?: Partial<LocalUserChoices>;
25
+ /** Display a debug window for your convenience. */
26
+ debug?: boolean;
27
+ joinLabel?: string;
28
+ micLabel?: string;
29
+ camLabel?: string;
30
+ userLabel?: string;
31
+ /**
32
+ * If true, user choices are persisted across sessions.
33
+ * @defaultValue true
34
+ * @alpha
35
+ */
36
+ persistUserChoices?: boolean;
37
+ }
38
+ /** @alpha */
39
+ export declare function usePreviewTracks(options: CreateLocalTracksOptions, onError?: (err: Error) => void): LocalTrack<Track.Kind>[];
40
+ /** @public */
41
+ export declare function usePreviewDevice<T extends LocalVideoTrack | LocalAudioTrack>(enabled: boolean, deviceId: string, kind: 'videoinput' | 'audioinput'): {
42
+ selectedDevice: MediaDeviceInfo;
43
+ localTrack: T;
44
+ deviceError: Error;
45
+ };
46
+ /**
47
+ * The `PreJoin` prefab component is normally presented to the user before he enters a room.
48
+ * This component allows the user to check and select the preferred media device (camera und microphone).
49
+ * On submit the user decisions are returned, which can then be passed on to the `LiveKitRoom` so that the user enters the room with the correct media devices.
50
+ *
51
+ * @remarks
52
+ * This component is independent of the `LiveKitRoom` component and should not be nested within it.
53
+ * Because it only accesses the local media tracks this component is self-contained and works without connection to the LiveKit server.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * <PreJoin />
58
+ * ```
59
+ * @public
60
+ */
61
+ export declare function PreJoin({ liveStream, defaults, onValidate, onSubmit, onError, debug, joinLabel, micLabel, camLabel, userLabel, persistUserChoices, ...htmlProps }: PreJoinProps): JSX.Element;
@@ -0,0 +1,241 @@
1
+ import { __awaiter, __rest } from "tslib";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { createLocalAudioTrack, createLocalTracks, createLocalVideoTrack, facingModeFromLocalTrack, Track, VideoPresets, Mutex } from 'livekit-client';
4
+ import * as React from 'react';
5
+ import { log } from '@livekit/components-core';
6
+ import { defaultUserChoices } from '@livekit/components-core';
7
+ import { MediaDeviceMenu, useMediaDevices, usePersistentUserChoices } from '@livekit/components-react';
8
+ import { useSCUser } from '@selfcommunity/react-core';
9
+ import ParticipantTileAvatar from './ParticipantTileAvatar';
10
+ import { useMemo } from 'react';
11
+ import { TrackToggle } from './TrackToggle';
12
+ /** @alpha */
13
+ export function usePreviewTracks(options, onError) {
14
+ const [tracks, setTracks] = React.useState();
15
+ const trackLock = React.useMemo(() => new Mutex(), []);
16
+ React.useEffect(() => {
17
+ let needsCleanup = false;
18
+ let localTracks = [];
19
+ trackLock.lock().then((unlock) => __awaiter(this, void 0, void 0, function* () {
20
+ try {
21
+ if (options.audio || options.video) {
22
+ localTracks = yield createLocalTracks(options);
23
+ if (needsCleanup) {
24
+ localTracks.forEach((tr) => tr.stop());
25
+ }
26
+ else {
27
+ setTracks(localTracks);
28
+ }
29
+ }
30
+ }
31
+ catch (e) {
32
+ if (onError && e instanceof Error) {
33
+ onError(e);
34
+ }
35
+ else {
36
+ log.error(e);
37
+ }
38
+ }
39
+ finally {
40
+ unlock();
41
+ }
42
+ }));
43
+ return () => {
44
+ needsCleanup = true;
45
+ localTracks.forEach((track) => {
46
+ track.stop();
47
+ });
48
+ };
49
+ }, [JSON.stringify(options), onError, trackLock]);
50
+ return tracks;
51
+ }
52
+ /** @public */
53
+ export function usePreviewDevice(enabled, deviceId, kind) {
54
+ const [deviceError, setDeviceError] = React.useState(null);
55
+ const [isCreatingTrack, setIsCreatingTrack] = React.useState(false);
56
+ const devices = useMediaDevices({ kind });
57
+ const [selectedDevice, setSelectedDevice] = React.useState(undefined);
58
+ const [localTrack, setLocalTrack] = React.useState();
59
+ const [localDeviceId, setLocalDeviceId] = React.useState(deviceId);
60
+ React.useEffect(() => {
61
+ setLocalDeviceId(deviceId);
62
+ }, [deviceId]);
63
+ const createTrack = (deviceId, kind) => __awaiter(this, void 0, void 0, function* () {
64
+ try {
65
+ const track = kind === 'videoinput'
66
+ ? yield createLocalVideoTrack({
67
+ deviceId: deviceId,
68
+ resolution: VideoPresets.h720.resolution
69
+ })
70
+ : yield createLocalAudioTrack({ deviceId });
71
+ const newDeviceId = yield track.getDeviceId();
72
+ if (newDeviceId && deviceId !== newDeviceId) {
73
+ prevDeviceId.current = newDeviceId;
74
+ setLocalDeviceId(newDeviceId);
75
+ }
76
+ setLocalTrack(track);
77
+ }
78
+ catch (e) {
79
+ if (e instanceof Error) {
80
+ setDeviceError(e);
81
+ }
82
+ }
83
+ });
84
+ const switchDevice = (track, id) => __awaiter(this, void 0, void 0, function* () {
85
+ yield track.setDeviceId(id);
86
+ prevDeviceId.current = id;
87
+ });
88
+ const prevDeviceId = React.useRef(localDeviceId);
89
+ React.useEffect(() => {
90
+ if (enabled && !localTrack && !deviceError && !isCreatingTrack) {
91
+ log.debug('creating track', kind);
92
+ setIsCreatingTrack(true);
93
+ createTrack(localDeviceId, kind).finally(() => {
94
+ setIsCreatingTrack(false);
95
+ });
96
+ }
97
+ }, [enabled, localTrack, deviceError, isCreatingTrack]);
98
+ // switch camera device
99
+ React.useEffect(() => {
100
+ if (!localTrack) {
101
+ return;
102
+ }
103
+ if (!enabled) {
104
+ log.debug(`muting ${kind} track`);
105
+ localTrack.mute().then(() => log.debug(localTrack.mediaStreamTrack));
106
+ }
107
+ else if ((selectedDevice === null || selectedDevice === void 0 ? void 0 : selectedDevice.deviceId) && prevDeviceId.current !== (selectedDevice === null || selectedDevice === void 0 ? void 0 : selectedDevice.deviceId)) {
108
+ log.debug(`switching ${kind} device from`, prevDeviceId.current, selectedDevice.deviceId);
109
+ switchDevice(localTrack, selectedDevice.deviceId);
110
+ }
111
+ else {
112
+ log.debug(`unmuting local ${kind} track`);
113
+ localTrack.unmute();
114
+ }
115
+ }, [localTrack, selectedDevice, enabled, kind]);
116
+ React.useEffect(() => {
117
+ return () => {
118
+ if (localTrack) {
119
+ log.debug(`stopping local ${kind} track`);
120
+ localTrack.stop();
121
+ localTrack.mute();
122
+ }
123
+ };
124
+ }, []);
125
+ React.useEffect(() => {
126
+ setSelectedDevice(devices === null || devices === void 0 ? void 0 : devices.find((dev) => dev.deviceId === localDeviceId));
127
+ }, [localDeviceId, devices]);
128
+ return {
129
+ selectedDevice,
130
+ localTrack,
131
+ deviceError
132
+ };
133
+ }
134
+ /**
135
+ * The `PreJoin` prefab component is normally presented to the user before he enters a room.
136
+ * This component allows the user to check and select the preferred media device (camera und microphone).
137
+ * On submit the user decisions are returned, which can then be passed on to the `LiveKitRoom` so that the user enters the room with the correct media devices.
138
+ *
139
+ * @remarks
140
+ * This component is independent of the `LiveKitRoom` component and should not be nested within it.
141
+ * Because it only accesses the local media tracks this component is self-contained and works without connection to the LiveKit server.
142
+ *
143
+ * @example
144
+ * ```tsx
145
+ * <PreJoin />
146
+ * ```
147
+ * @public
148
+ */
149
+ export function PreJoin(_a) {
150
+ var { liveStream, defaults = {}, onValidate, onSubmit, onError, debug, joinLabel = 'Join Room', micLabel = 'Microphone', camLabel = 'Camera', userLabel = 'Username', persistUserChoices = true } = _a, htmlProps = __rest(_a, ["liveStream", "defaults", "onValidate", "onSubmit", "onError", "debug", "joinLabel", "micLabel", "camLabel", "userLabel", "persistUserChoices"]);
151
+ const [userChoices, setUserChoices] = React.useState(defaultUserChoices);
152
+ // TODO: Remove and pipe `defaults` object directly into `usePersistentUserChoices` once we fully switch from type `LocalUserChoices` to `UserChoices`.
153
+ const partialDefaults = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (defaults.audioDeviceId !== undefined && { audioDeviceId: defaults.audioDeviceId })), (defaults.videoDeviceId !== undefined && { videoDeviceId: defaults.videoDeviceId })), (defaults.audioEnabled !== undefined && { audioEnabled: defaults.audioEnabled })), (defaults.videoEnabled !== undefined && { videoEnabled: defaults.videoEnabled })), (defaults.username !== undefined && { username: defaults.username }));
154
+ const { userChoices: initialUserChoices, saveAudioInputDeviceId, saveAudioInputEnabled, saveVideoInputDeviceId, saveVideoInputEnabled, saveUsername } = usePersistentUserChoices({
155
+ defaults: partialDefaults,
156
+ preventSave: !persistUserChoices,
157
+ preventLoad: !persistUserChoices
158
+ });
159
+ // Initialize device settings
160
+ const [audioEnabled, setAudioEnabled] = React.useState(initialUserChoices.audioEnabled);
161
+ const [videoEnabled, setVideoEnabled] = React.useState(initialUserChoices.videoEnabled);
162
+ const [audioDeviceId, setAudioDeviceId] = React.useState(initialUserChoices.audioDeviceId);
163
+ const [videoDeviceId, setVideoDeviceId] = React.useState(initialUserChoices.videoDeviceId);
164
+ const [username, setUsername] = React.useState(initialUserChoices.username);
165
+ // Save user choices to persistent storage.
166
+ React.useEffect(() => {
167
+ saveAudioInputEnabled(audioEnabled);
168
+ }, [audioEnabled, saveAudioInputEnabled]);
169
+ React.useEffect(() => {
170
+ saveVideoInputEnabled(videoEnabled);
171
+ }, [videoEnabled, saveVideoInputEnabled]);
172
+ React.useEffect(() => {
173
+ saveAudioInputDeviceId(audioDeviceId);
174
+ }, [audioDeviceId, saveAudioInputDeviceId]);
175
+ React.useEffect(() => {
176
+ saveVideoInputDeviceId(videoDeviceId);
177
+ }, [videoDeviceId, saveVideoInputDeviceId]);
178
+ React.useEffect(() => {
179
+ saveUsername(username);
180
+ }, [username, saveUsername]);
181
+ const tracks = usePreviewTracks({
182
+ audio: audioEnabled ? { deviceId: initialUserChoices.audioDeviceId } : false,
183
+ video: videoEnabled ? { deviceId: initialUserChoices.videoDeviceId } : false
184
+ }, onError);
185
+ const scUserContext = useSCUser();
186
+ const videoEl = React.useRef(null);
187
+ const videoTrack = React.useMemo(() => tracks === null || tracks === void 0 ? void 0 : tracks.filter((track) => track.kind === Track.Kind.Video)[0], [tracks]);
188
+ const facingMode = React.useMemo(() => {
189
+ if (videoTrack) {
190
+ const { facingMode } = facingModeFromLocalTrack(videoTrack);
191
+ return facingMode;
192
+ }
193
+ else {
194
+ return 'undefined';
195
+ }
196
+ }, [videoTrack]);
197
+ const audioTrack = React.useMemo(() => tracks === null || tracks === void 0 ? void 0 : tracks.filter((track) => track.kind === Track.Kind.Audio)[0], [tracks]);
198
+ React.useEffect(() => {
199
+ if (videoEl.current && videoTrack) {
200
+ videoTrack.unmute();
201
+ videoTrack.attach(videoEl.current);
202
+ }
203
+ return () => {
204
+ videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.detach();
205
+ };
206
+ }, [videoTrack]);
207
+ const [isValid, setIsValid] = React.useState();
208
+ const handleValidation = React.useCallback((values) => {
209
+ if (typeof onValidate === 'function') {
210
+ return onValidate(values);
211
+ }
212
+ else {
213
+ return values.username !== '';
214
+ }
215
+ }, [onValidate]);
216
+ React.useEffect(() => {
217
+ const newUserChoices = {
218
+ username,
219
+ videoEnabled,
220
+ videoDeviceId,
221
+ audioEnabled,
222
+ audioDeviceId
223
+ };
224
+ setUserChoices(newUserChoices);
225
+ setIsValid(handleValidation(newUserChoices));
226
+ }, [username, videoEnabled, handleValidation, audioEnabled, audioDeviceId, videoDeviceId]);
227
+ function handleSubmit(event) {
228
+ event.preventDefault();
229
+ if (handleValidation(userChoices)) {
230
+ if (typeof onSubmit === 'function') {
231
+ onSubmit(userChoices);
232
+ }
233
+ }
234
+ else {
235
+ log.warn('Validation failed with: ', userChoices);
236
+ }
237
+ }
238
+ const canUseAudio = useMemo(() => { var _a; return (scUserContext.user && liveStream && liveStream.host.id === scUserContext.user.id) || (liveStream && !((_a = liveStream === null || liveStream === void 0 ? void 0 : liveStream.settings) === null || _a === void 0 ? void 0 : _a.muteParticipant)); }, [scUserContext, liveStream]);
239
+ const canUseVideo = useMemo(() => { var _a; return (scUserContext.user && liveStream && liveStream.host.id === scUserContext.user.id) || (liveStream && !((_a = liveStream === null || liveStream === void 0 ? void 0 : liveStream.settings) === null || _a === void 0 ? void 0 : _a.disableVideo)); }, [scUserContext, liveStream]);
240
+ 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, 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, tracks: { videoinput: videoTrack }, onActiveDeviceChange: (_, id) => setVideoDeviceId(id) }) }))] }))] })), _jsxs("form", Object.assign({ className: "lk-username-container" }, { children: [_jsx("input", { className: "lk-form-control", id: "username", name: "username", type: "text", defaultValue: username, placeholder: userLabel, onChange: (inputEl) => setUsername(inputEl.target.value), autoComplete: "off" }), _jsx("button", Object.assign({ className: "lk-button lk-join-button", type: "submit", onClick: handleSubmit, disabled: !isValid }, { 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}`] })] }))] }))] })));
241
+ }
@@ -0,0 +1,23 @@
1
+ import type { CaptureOptionsBySource, ToggleSource } from '@livekit/components-core';
2
+ import * as React from 'react';
3
+ import { Track, TrackPublishOptions } from 'livekit-client';
4
+ export declare function getSourceIcon(source: Track.Source, enabled: boolean): JSX.Element;
5
+ export interface TrackToggleProps<T extends ToggleSource> extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
6
+ source: T;
7
+ showIcon?: boolean;
8
+ initialState?: boolean;
9
+ disabled?: boolean;
10
+ /**
11
+ * Function that is called when the enabled state of the toggle changes.
12
+ * The second function argument `isUserInitiated` is `true` if the change was initiated by a user interaction, such as a click.
13
+ */
14
+ onChange?: (enabled: boolean, isUserInitiated: boolean) => void;
15
+ captureOptions?: CaptureOptionsBySource<T>;
16
+ publishOptions?: TrackPublishOptions;
17
+ onDeviceError?: (error: Error) => void;
18
+ }
19
+ /**
20
+ * With the `TrackToggle` component it is possible to mute and unmute your camera and microphone.
21
+ * The component uses an html button element under the hood so you can treat it like a button.
22
+ */
23
+ export declare const TrackToggle: <T extends ToggleSource>(props: TrackToggleProps<T> & React.RefAttributes<HTMLButtonElement>) => React.ReactNode;
@@ -0,0 +1,27 @@
1
+ import { __rest } from "tslib";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { Track } from 'livekit-client';
5
+ import { useTrackToggle, CameraIcon, CameraDisabledIcon, MicDisabledIcon, MicIcon, ScreenShareIcon, ScreenShareStopIcon } from '@livekit/components-react';
6
+ export function getSourceIcon(source, enabled) {
7
+ switch (source) {
8
+ case Track.Source.Microphone:
9
+ return enabled ? _jsx(MicIcon, {}) : _jsx(MicDisabledIcon, {});
10
+ case Track.Source.Camera:
11
+ return enabled ? _jsx(CameraIcon, {}) : _jsx(CameraDisabledIcon, {});
12
+ case Track.Source.ScreenShare:
13
+ return enabled ? _jsx(ScreenShareStopIcon, {}) : _jsx(ScreenShareIcon, {});
14
+ default:
15
+ return undefined;
16
+ }
17
+ }
18
+ /**
19
+ * With the `TrackToggle` component it is possible to mute and unmute your camera and microphone.
20
+ * The component uses an html button element under the hood so you can treat it like a button.
21
+ */
22
+ export const TrackToggle =
23
+ /* @__PURE__ */ React.forwardRef(function TrackToggle(_a, ref) {
24
+ var { showIcon, disabled } = _a, props = __rest(_a, ["showIcon", "disabled"]);
25
+ const { buttonProps, enabled } = useTrackToggle(props);
26
+ return (_jsxs("button", Object.assign({ ref: ref }, buttonProps, { disabled: disabled }, { children: [(showIcon !== null && showIcon !== void 0 ? showIcon : true) && getSourceIcon(props.source, enabled && !disabled), props.children] })));
27
+ });
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import type { MessageDecoder, MessageEncoder } from '@livekit/components-core';
3
3
  import { MessageFormatter } from '@livekit/components-react';
4
+ import { SCUserType } from '@selfcommunity/types';
4
5
  /**
5
6
  * @public
6
7
  */
@@ -10,11 +11,11 @@ export interface VideoConferenceProps extends React.HTMLAttributes<HTMLDivElemen
10
11
  chatMessageDecoder?: MessageDecoder;
11
12
  /** @alpha */
12
13
  SettingsComponent?: React.ComponentType;
13
- speakerFocused?: string;
14
+ speakerFocused?: SCUserType;
14
15
  disableChat?: boolean;
15
16
  disableMicrophone?: boolean;
16
17
  disableCamera?: boolean;
17
- disableScreenShare?: boolean;
18
+ disableShareScreen?: boolean;
18
19
  hideParticipantList?: boolean;
19
20
  showSettings?: boolean;
20
21
  }
@@ -36,4 +37,4 @@ export interface VideoConferenceProps extends React.HTMLAttributes<HTMLDivElemen
36
37
  * ```
37
38
  * @public
38
39
  */
39
- export declare function VideoConference({ chatMessageFormatter, chatMessageDecoder, chatMessageEncoder, SettingsComponent, speakerFocused, disableChat, disableMicrophone, disableCamera, disableScreenShare, hideParticipantList, showSettings, ...props }: VideoConferenceProps): JSX.Element;
40
+ export declare function VideoConference({ chatMessageFormatter, chatMessageDecoder, chatMessageEncoder, SettingsComponent, speakerFocused, disableChat, disableMicrophone, disableCamera, disableShareScreen, hideParticipantList, showSettings, ...props }: VideoConferenceProps): JSX.Element;