feat: add livekit meeting context and

chore/devcontainer
moonrailgun 2 years ago
parent 7e239ead86
commit c937178956

@ -11,6 +11,7 @@ import { useLivekitState } from '../store/useLivekitState';
import { useServerUrl } from '../utils/useServerUrl'; import { useServerUrl } from '../utils/useServerUrl';
import { useToken } from '../utils/useToken'; import { useToken } from '../utils/useToken';
import { VideoConference } from './lib/VideoConference'; import { VideoConference } from './lib/VideoConference';
import { MeetingContextProvider } from '../context/MeetingContext';
const UpdateRoom: React.FC = React.memo(() => { const UpdateRoom: React.FC = React.memo(() => {
const room = useRoomContext(); const room = useRoomContext();
@ -71,7 +72,9 @@ export const ActiveRoom: React.FC<ActiveRoomProps> = React.memo((props) => {
audio={userChoices.audioEnabled} audio={userChoices.audioEnabled}
onDisconnected={onLeave} onDisconnected={onLeave}
> >
<VideoConference chatMessageFormatter={formatChatMessageLinks} /> <MeetingContextProvider>
<VideoConference chatMessageFormatter={formatChatMessageLinks} />
</MeetingContextProvider>
<UpdateRoom /> <UpdateRoom />
</LiveKitRoom> </LiveKitRoom>

@ -15,6 +15,7 @@ import { Translate } from '../../translate';
import { useMediaQuery } from '../../utils/useMediaQuery'; import { useMediaQuery } from '../../utils/useMediaQuery';
import { ChatIcon } from './icons/ChatIcon'; import { ChatIcon } from './icons/ChatIcon';
import { LeaveIcon } from './icons/LeaveIcon'; import { LeaveIcon } from './icons/LeaveIcon';
import { useMeetingContextState } from '../../context/MeetingContext';
/** @public */ /** @public */
export type ControlBarControls = { export type ControlBarControls = {
@ -55,6 +56,7 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) {
setIsChatOpen(layoutContext?.widget.state?.showChat); setIsChatOpen(layoutContext?.widget.state?.showChat);
} }
}, [layoutContext?.widget.state?.showChat]); }, [layoutContext?.widget.state?.showChat]);
const setRightPanel = useMeetingContextState((state) => state.setRightPanel);
const isTooLittleSpace = useMediaQuery( const isTooLittleSpace = useMediaQuery(
`(max-width: ${isChatOpen ? 1000 : 760}px)` `(max-width: ${isChatOpen ? 1000 : 760}px)`
); );
@ -134,10 +136,10 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) {
)} )}
{visibleControls.chat && ( {visibleControls.chat && (
<ChatToggle> <button className="lk-button" onClick={() => setRightPanel('chat')}>
{showIcon && <ChatIcon />} {showIcon && <ChatIcon />}
{showText && Translate.chat} {showText && Translate.chat}
</ChatToggle> </button>
)} )}
{visibleControls.leave && ( {visibleControls.leave && (

@ -28,6 +28,7 @@ import { CarouselLayout } from './CarouselLayout';
import { ControlBar } from './ControlBar'; import { ControlBar } from './ControlBar';
import { Chat } from './Chat'; import { Chat } from './Chat';
import { FocusLayout } from './FocusLayout'; import { FocusLayout } from './FocusLayout';
import { useMeetingContextState } from '../../context/MeetingContext';
/** /**
* @public * @public
@ -60,6 +61,7 @@ export const VideoConference: React.FC<VideoConferenceProps> = React.memo(
}); });
const lastAutoFocusedScreenShareTrack = const lastAutoFocusedScreenShareTrack =
React.useRef<TrackReferenceOrPlaceholder | null>(null); React.useRef<TrackReferenceOrPlaceholder | null>(null);
const rightPanel = useMeetingContextState((state) => state.rightPanel);
const tracks = useTracks( const tracks = useTracks(
[ [
@ -146,10 +148,9 @@ export const VideoConference: React.FC<VideoConferenceProps> = React.memo(
<ControlBar controls={{ chat: true }} /> <ControlBar controls={{ chat: true }} />
</div> </div>
<Chat {rightPanel === 'chat' && (
style={{ display: widgetState.showChat ? 'flex' : 'none' }} <Chat messageFormatter={chatMessageFormatter} />
messageFormatter={chatMessageFormatter} )}
/>
</LayoutContextProvider> </LayoutContextProvider>
)} )}

@ -0,0 +1,28 @@
import React, { PropsWithChildren, useContext, useMemo } from 'react';
import {
MeetingState,
MeetingStateStoreType,
createMeetingStateStore,
} from '../store/meeting';
import { useStore } from 'zustand';
const MeetingContext = React.createContext<MeetingStateStoreType>(null);
export const MeetingContextProvider: React.FC<PropsWithChildren> = React.memo(
(props) => {
const store = useMemo(() => createMeetingStateStore(), []);
return (
<MeetingContext.Provider value={store}>
{props.children}
</MeetingContext.Provider>
);
}
);
MeetingContextProvider.displayName = 'MeetingContextProvider';
export function useMeetingContextState<T>(selector: (s: MeetingState) => T) {
const context = useContext(MeetingContext);
return useStore(context, selector);
}

@ -0,0 +1,25 @@
import { createStore } from 'zustand';
import { immer } from 'zustand/middleware/immer';
export interface MeetingState {
rightPanel: 'chat' | 'member' | null;
setRightPanel: (panel: 'chat' | 'member' | null) => void;
}
export function createMeetingStateStore() {
return createStore<MeetingState>()(
immer((set, get) => ({
rightPanel: null,
setRightPanel: (rightPanel) => {
if (get().rightPanel === rightPanel) {
// toggle
set({ rightPanel: null });
} else {
set({ rightPanel });
}
},
}))
);
}
export type MeetingStateStoreType = ReturnType<typeof createMeetingStateStore>;
Loading…
Cancel
Save