display class state in the header
This commit is contained in:
parent
41b7f8e7e2
commit
c052ff45a0
|
|
@ -5,9 +5,9 @@ import { useCallUI } from '@custom/shared/hooks/useCallUI';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { ChatProvider } from '../../contexts/ChatProvider';
|
import { ChatProvider } from '../../contexts/ChatProvider';
|
||||||
|
import { ClassStateProvider } from '../../contexts/ClassStateProvider';
|
||||||
import { LiveStreamingProvider } from '../../contexts/LiveStreamingProvider';
|
import { LiveStreamingProvider } from '../../contexts/LiveStreamingProvider';
|
||||||
import { RecordingProvider } from '../../contexts/RecordingProvider';
|
import { RecordingProvider } from '../../contexts/RecordingProvider';
|
||||||
import { ViewProvider } from '../../contexts/ViewProvider';
|
|
||||||
import Room from '../Call/Room';
|
import Room from '../Call/Room';
|
||||||
import { Asides } from './Asides';
|
import { Asides } from './Asides';
|
||||||
import { Modals } from './Modals';
|
import { Modals } from './Modals';
|
||||||
|
|
@ -28,7 +28,7 @@ export const App = ({ customComponentForState }) => {
|
||||||
<ChatProvider>
|
<ChatProvider>
|
||||||
<RecordingProvider>
|
<RecordingProvider>
|
||||||
<LiveStreamingProvider>
|
<LiveStreamingProvider>
|
||||||
<ViewProvider>
|
<ClassStateProvider>
|
||||||
{roomExp && <ExpiryTimer expiry={roomExp} />}
|
{roomExp && <ExpiryTimer expiry={roomExp} />}
|
||||||
<div className="app">
|
<div className="app">
|
||||||
{componentForState()}
|
{componentForState()}
|
||||||
|
|
@ -46,7 +46,7 @@ export const App = ({ customComponentForState }) => {
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
</ViewProvider>
|
</ClassStateProvider>
|
||||||
</LiveStreamingProvider>
|
</LiveStreamingProvider>
|
||||||
</RecordingProvider>
|
</RecordingProvider>
|
||||||
</ChatProvider>
|
</ChatProvider>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,44 @@
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo, useCallback } from 'react';
|
||||||
|
import Button from '@custom/shared/components/Button';
|
||||||
import HeaderCapsule from '@custom/shared/components/HeaderCapsule';
|
import HeaderCapsule from '@custom/shared/components/HeaderCapsule';
|
||||||
import { useCallState } from '@custom/shared/contexts/CallProvider';
|
import { useCallState } from '@custom/shared/contexts/CallProvider';
|
||||||
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
|
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
|
||||||
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
|
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
|
||||||
import { ReactComponent as IconLock } from '@custom/shared/icons/lock-md.svg';
|
import { ReactComponent as IconLock } from '@custom/shared/icons/lock-md.svg';
|
||||||
|
import { ReactComponent as IconPlay } from '@custom/shared/icons/play-sm.svg';
|
||||||
import { slugify } from '@custom/shared/lib/slugify';
|
import { slugify } from '@custom/shared/lib/slugify';
|
||||||
|
import { useClassState, PRE_CLASS_LOBBY, CLASS_IN_SESSION } from '../../contexts/ClassStateProvider';
|
||||||
|
|
||||||
export const Header = () => {
|
export const Header = () => {
|
||||||
const { roomInfo } = useCallState();
|
const { roomInfo } = useCallState();
|
||||||
const { participantCount } = useParticipants();
|
const { participantCount, localParticipant } = useParticipants();
|
||||||
const { customCapsule } = useUIState();
|
const { customCapsule } = useUIState();
|
||||||
|
const { classType, setClassType } = useClassState();
|
||||||
|
|
||||||
|
const capsuleLabel = useCallback(() => {
|
||||||
|
if (!localParticipant.isOwner) return;
|
||||||
|
if (classType === PRE_CLASS_LOBBY)
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
IconBefore={IconPlay}
|
||||||
|
size="tiny"
|
||||||
|
variant="success"
|
||||||
|
onClick={setClassType}
|
||||||
|
>
|
||||||
|
Start Class
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
if (classType === CLASS_IN_SESSION)
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="tiny"
|
||||||
|
variant="error-light"
|
||||||
|
onClick={setClassType}
|
||||||
|
>
|
||||||
|
End Class
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}, [classType, localParticipant.isOwner, setClassType]);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
|
@ -37,6 +66,10 @@ export const Header = () => {
|
||||||
{customCapsule.label}
|
{customCapsule.label}
|
||||||
</HeaderCapsule>
|
</HeaderCapsule>
|
||||||
)}
|
)}
|
||||||
|
<HeaderCapsule>
|
||||||
|
{classType}
|
||||||
|
{capsuleLabel()}
|
||||||
|
</HeaderCapsule>
|
||||||
|
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.room-header {
|
.room-header {
|
||||||
|
|
@ -56,7 +89,7 @@ export const Header = () => {
|
||||||
`}</style>
|
`}</style>
|
||||||
</header>
|
</header>
|
||||||
),
|
),
|
||||||
[roomInfo.privacy, roomInfo.name, participantCount, customCapsule]
|
[roomInfo.privacy, roomInfo.name, participantCount, customCapsule, classType, capsuleLabel]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { TrayButton } from '@custom/shared/components/Tray';
|
|
||||||
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
|
|
||||||
import { VIEW_MODE_GRID, VIEW_MODE_SPEAKER } from '@custom/shared/contexts/UIStateProvider';
|
|
||||||
import { ReactComponent as IconGridView } from '@custom/shared/icons/grid-md.svg';
|
|
||||||
import { ReactComponent as IconSpeakerView } from '@custom/shared/icons/speaker-view-md.svg';
|
|
||||||
import { useView } from '../../contexts/ViewProvider';
|
|
||||||
|
|
||||||
export const ViewTray = () => {
|
|
||||||
const { participants, localParticipant } = useParticipants();
|
|
||||||
const { view, setView } = useView();
|
|
||||||
|
|
||||||
const onViewClick = () =>
|
|
||||||
setView(view === VIEW_MODE_SPEAKER ? VIEW_MODE_GRID : VIEW_MODE_SPEAKER);
|
|
||||||
|
|
||||||
if (!localParticipant.isOwner) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TrayButton
|
|
||||||
label={view === VIEW_MODE_GRID ? 'Speaker': 'Grid'}
|
|
||||||
disabled={participants.length < 2}
|
|
||||||
onClick={onViewClick}
|
|
||||||
>
|
|
||||||
{view === VIEW_MODE_SPEAKER ? <IconGridView />: <IconSpeakerView />}
|
|
||||||
</TrayButton>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ViewTray;
|
|
||||||
|
|
@ -3,7 +3,6 @@ import ChatTray from './Chat';
|
||||||
import RecordTray from './Record';
|
import RecordTray from './Record';
|
||||||
import ScreenShareTray from './ScreenShare';
|
import ScreenShareTray from './ScreenShare';
|
||||||
import StreamTray from './Stream';
|
import StreamTray from './Stream';
|
||||||
import ViewTray from './View';
|
|
||||||
|
|
||||||
export const Tray = () => {
|
export const Tray = () => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -12,7 +11,6 @@ export const Tray = () => {
|
||||||
<ScreenShareTray />
|
<ScreenShareTray />
|
||||||
<RecordTray />
|
<RecordTray />
|
||||||
<StreamTray />
|
<StreamTray />
|
||||||
<ViewTray />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
useEffect,
|
||||||
|
} from 'react';
|
||||||
|
import { useUIState, VIEW_MODE_SPEAKER, VIEW_MODE_GRID } from '@custom/shared/contexts/UIStateProvider';
|
||||||
|
import { useSharedState } from '@custom/shared/hooks/useSharedState';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
export const PRE_CLASS_LOBBY = 'Pre-class lobby';
|
||||||
|
export const CLASS_IN_SESSION = 'Class-in session';
|
||||||
|
export const POST_CLASS_LOBBY = 'Post-class lobby';
|
||||||
|
|
||||||
|
export const ClassStateContext = createContext();
|
||||||
|
|
||||||
|
export const ClassStateProvider = ({ children }) => {
|
||||||
|
const { setPreferredViewMode } = useUIState();
|
||||||
|
|
||||||
|
const { sharedState, setSharedState } = useSharedState({
|
||||||
|
initialValues: { type: PRE_CLASS_LOBBY },
|
||||||
|
});
|
||||||
|
|
||||||
|
const classType = useMemo(() => sharedState.type, [sharedState.type]);
|
||||||
|
|
||||||
|
const setClassType = useCallback(() => {
|
||||||
|
if (sharedState.type === PRE_CLASS_LOBBY) setSharedState({ type: CLASS_IN_SESSION });
|
||||||
|
if (sharedState.type === CLASS_IN_SESSION) setSharedState({ type: POST_CLASS_LOBBY });
|
||||||
|
}, [sharedState.type, setSharedState]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (sharedState.type === CLASS_IN_SESSION) setPreferredViewMode(VIEW_MODE_SPEAKER);
|
||||||
|
else setPreferredViewMode(VIEW_MODE_GRID);
|
||||||
|
}, [setPreferredViewMode, sharedState.type]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ClassStateContext.Provider value={{ classType, setClassType }}>
|
||||||
|
{children}
|
||||||
|
</ClassStateContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ClassStateProvider.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useClassState = () => useContext(ClassStateContext);
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import React, {
|
|
||||||
createContext,
|
|
||||||
useContext,
|
|
||||||
useCallback,
|
|
||||||
useMemo,
|
|
||||||
useEffect,
|
|
||||||
} from 'react';
|
|
||||||
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
|
|
||||||
import { useSharedState } from '@custom/shared/hooks/useSharedState';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
export const ViewContext = createContext();
|
|
||||||
|
|
||||||
export const ViewProvider = ({ children }) => {
|
|
||||||
const { viewMode, setPreferredViewMode } = useUIState();
|
|
||||||
|
|
||||||
const { sharedState, setSharedState } = useSharedState({
|
|
||||||
initialValues: { viewMode },
|
|
||||||
});
|
|
||||||
|
|
||||||
const view = useMemo(() => sharedState.viewMode, [sharedState.viewMode]);
|
|
||||||
const setView = useCallback((view) => setSharedState({ viewMode: view }), [setSharedState]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (viewMode === sharedState.viewMode) return;
|
|
||||||
setPreferredViewMode(sharedState.viewMode);
|
|
||||||
}, [setPreferredViewMode, sharedState.viewMode, viewMode]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ViewContext.Provider value={{ view, setView }}>
|
|
||||||
{children}
|
|
||||||
</ViewContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ViewProvider.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useView = () => useContext(ViewContext);
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="9" height="12" viewBox="0 0 9 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0.783 0.0880035C0.707928 0.0364627 0.620207 0.00640194 0.529301 0.00106545C0.438396 -0.00427103 0.34776 0.0153194 0.267173 0.0577225C0.186587 0.100126 0.11911 0.163731 0.0720258 0.241675C0.0249413 0.319619 3.68967e-05 0.408942 0 0.500004V11.5C0.000167265 11.5911 0.02522 11.6804 0.0724523 11.7583C0.119685 11.8362 0.187301 11.8997 0.268 11.942C0.339356 11.9802 0.419063 12.0001 0.5 12C0.601059 11.9999 0.699728 11.9693 0.783 11.912L8.783 6.412C8.84983 6.36605 8.90447 6.30454 8.94222 6.23276C8.97998 6.16098 8.99971 6.0811 8.99971 6C8.99971 5.9189 8.97998 5.83902 8.94222 5.76725C8.90447 5.69547 8.84983 5.63395 8.783 5.588L0.783 0.0880035Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 768 B |
Loading…
Reference in New Issue