...
This commit is contained in:
154
lib/SettingsMenu.tsx
Normal file
154
lib/SettingsMenu.tsx
Normal file
@@ -0,0 +1,154 @@
|
||||
'use client';
|
||||
import * as React from 'react';
|
||||
import { Track } from 'livekit-client';
|
||||
import {
|
||||
useMaybeLayoutContext,
|
||||
MediaDeviceMenu,
|
||||
TrackToggle,
|
||||
useRoomContext,
|
||||
useIsRecording,
|
||||
} from '@livekit/components-react';
|
||||
import styles from '../styles/SettingsMenu.module.css';
|
||||
import { CameraSettings } from './CameraSettings';
|
||||
import { MicrophoneSettings } from './MicrophoneSettings';
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export interface SettingsMenuProps extends React.HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export function SettingsMenu(props: SettingsMenuProps) {
|
||||
const layoutContext = useMaybeLayoutContext();
|
||||
const room = useRoomContext();
|
||||
const recordingEndpoint = process.env.NEXT_PUBLIC_LK_RECORD_ENDPOINT;
|
||||
|
||||
const settings = React.useMemo(() => {
|
||||
return {
|
||||
media: { camera: true, microphone: true, label: 'Media Devices', speaker: true },
|
||||
recording: recordingEndpoint ? { label: 'Recording' } : undefined,
|
||||
};
|
||||
}, []);
|
||||
|
||||
const tabs = React.useMemo(
|
||||
() => Object.keys(settings).filter((t) => t !== undefined) as Array<keyof typeof settings>,
|
||||
[settings],
|
||||
);
|
||||
const [activeTab, setActiveTab] = React.useState(tabs[0]);
|
||||
|
||||
const isRecording = useIsRecording();
|
||||
const [initialRecStatus, setInitialRecStatus] = React.useState(isRecording);
|
||||
const [processingRecRequest, setProcessingRecRequest] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (initialRecStatus !== isRecording) {
|
||||
setProcessingRecRequest(false);
|
||||
}
|
||||
}, [isRecording, initialRecStatus]);
|
||||
|
||||
const toggleRoomRecording = async () => {
|
||||
if (!recordingEndpoint) {
|
||||
throw TypeError('No recording endpoint specified');
|
||||
}
|
||||
if (room.isE2EEEnabled) {
|
||||
throw Error('Recording of encrypted meetings is currently not supported');
|
||||
}
|
||||
setProcessingRecRequest(true);
|
||||
setInitialRecStatus(isRecording);
|
||||
let response: Response;
|
||||
if (isRecording) {
|
||||
response = await fetch(recordingEndpoint + `/stop?roomName=${room.name}`);
|
||||
} else {
|
||||
response = await fetch(recordingEndpoint + `/start?roomName=${room.name}`);
|
||||
}
|
||||
if (response.ok) {
|
||||
} else {
|
||||
console.error(
|
||||
'Error handling recording request, check server logs:',
|
||||
response.status,
|
||||
response.statusText,
|
||||
);
|
||||
setProcessingRecRequest(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="settings-menu" style={{ width: '100%', position: 'relative' }} {...props}>
|
||||
<div className={styles.tabs}>
|
||||
{tabs.map(
|
||||
(tab) =>
|
||||
settings[tab] && (
|
||||
<button
|
||||
className={`${styles.tab} lk-button`}
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
aria-pressed={tab === activeTab}
|
||||
>
|
||||
{
|
||||
// @ts-ignore
|
||||
settings[tab].label
|
||||
}
|
||||
</button>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
<div className="tab-content">
|
||||
{activeTab === 'media' && (
|
||||
<>
|
||||
{settings.media && settings.media.camera && (
|
||||
<>
|
||||
<h3>Camera</h3>
|
||||
<section>
|
||||
<CameraSettings />
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
{settings.media && settings.media.microphone && (
|
||||
<>
|
||||
<h3>Microphone</h3>
|
||||
<section>
|
||||
<MicrophoneSettings />
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
{settings.media && settings.media.speaker && (
|
||||
<>
|
||||
<h3>Speaker & Headphones</h3>
|
||||
<section className="lk-button-group">
|
||||
<span className="lk-button">Audio Output</span>
|
||||
<div className="lk-button-group-menu">
|
||||
<MediaDeviceMenu kind="audiooutput"></MediaDeviceMenu>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{activeTab === 'recording' && (
|
||||
<>
|
||||
<h3>Record Meeting</h3>
|
||||
<section>
|
||||
<p>
|
||||
{isRecording
|
||||
? 'Meeting is currently being recorded'
|
||||
: 'No active recordings for this meeting'}
|
||||
</p>
|
||||
<button disabled={processingRecRequest} onClick={() => toggleRoomRecording()}>
|
||||
{isRecording ? 'Stop' : 'Start'} Recording
|
||||
</button>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', width: '100%' }}>
|
||||
<button
|
||||
className={`lk-button`}
|
||||
onClick={() => layoutContext?.widget.dispatch?.({ msg: 'toggle_settings' })}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user