adding fullscreen support to video calls

This commit is contained in:
Roland Osborne 2024-03-06 13:34:00 -08:00
parent 48baa779fb
commit f03ca2dbe0
3 changed files with 147 additions and 87 deletions

View File

@ -14,11 +14,13 @@ import { Account } from './account/Account';
import { Welcome } from './welcome/Welcome'; import { Welcome } from './welcome/Welcome';
import { BottomNav } from './bottomNav/BottomNav'; import { BottomNav } from './bottomNav/BottomNav';
import { Logo } from 'logo/Logo'; import { Logo } from 'logo/Logo';
import { EyeInvisibleOutlined, PhoneOutlined } from '@ant-design/icons'; import { EyeInvisibleOutlined, PhoneOutlined, ShrinkOutlined, ArrowsAltOutlined } from '@ant-design/icons';
import { IoVideocamOffOutline, IoVideocamOutline, IoMicOffOutline, IoMicOutline, IoCallOutline } from "react-icons/io5"; import { IoVideocamOffOutline, IoVideocamOutline, IoMicOffOutline, IoMicOutline, IoCallOutline } from "react-icons/io5";
import { ThemeProvider } from "styled-components"; import { ThemeProvider } from "styled-components";
import { SettingsContext } from 'context/SettingsContext'; import { SettingsContext } from 'context/SettingsContext';
import avatar from 'images/avatar.png';
export function Session() { export function Session() {
const { state, actions } = useSession(); const { state, actions } = useSession();
@ -347,43 +349,64 @@ export function Session() {
</Modal> </Modal>
<Modal centered visible={state.callStatus} footer={null} closable={false} width={callModal.width} height={callModal.height} bodyStyle={{ padding: 6 }}> <Modal centered visible={state.callStatus} footer={null} closable={false} width={callModal.width} height={callModal.height} bodyStyle={{ padding: 6 }}>
<CallingWrapper> <CallingWrapper>
{ !state.remoteVideo && ( <div className={ state.fullscreen ? 'fullscreen' : 'modal' }>
<Logo url={state.callLogo} width={256} height={256} radius={8} /> <div className="window">
)} { !state.remoteVideo && (
{ state.remoteStream && ( <div className="logo">
<video ref={remote} disablepictureinpicture playsInline autoPlay style={{ display: state.remoteVideo ? 'block' : 'none', width: '100%' }} { state.callLogo && (
complete={() => console.log("VIDEO COMPLETE")} progress={() => console.log("VIDEO PROGRESS")} error={() => console.log("VIDEO ERROR")} waiting={() => console.log("VIDEO WAITING")} /> <img className="image" src={state.callLogo} alt="logo" width="100%" height="100%" />
)} )}
{ state.localStream && ( { !state.callLogo && (
<div className="calling-local"> <img className="image" src={avatar} alt="default logo" width="100%" height="100%" />
<video ref={local} disablepictureinpicture playsInline autoPlay muted style={{ width: '100%', display: 'block' }} )}
complete={() => console.log("VIDEO COMPLETE")} progress={() => console.log("VIDEO PROGRESS")} error={() => console.log("VIDEO ERROR")} waiting={() => console.log("VIDEO WAITING")} /> </div>
)}
{ state.remoteStream && (
<video ref={remote} disablepictureinpicture playsInline autoPlay style={{ display: state.remoteVideo ? 'block' : 'none', width: '100%', height: '100%' }}
complete={() => console.log("VIDEO COMPLETE")} progress={() => console.log("VIDEO PROGRESS")} error={() => console.log("VIDEO ERROR")} waiting={() => console.log("VIDEO WAITING")} />
)}
{ state.localStream && (
<div className="calling-local">
<video ref={local} disablepictureinpicture playsInline autoPlay muted style={{ width: '100%', height: '100%', display: 'block' }}
complete={() => console.log("VIDEO COMPLETE")} progress={() => console.log("VIDEO PROGRESS")} error={() => console.log("VIDEO ERROR")} waiting={() => console.log("VIDEO WAITING")} />
</div>
)}
<div className="calling-options calling-hovered">
{ state.localVideo && (
<div className="calling-option" onClick={actions.disableVideo}>
<IoVideocamOutline />
</div>
)}
{ !state.localVideo && (
<div className="calling-option" onClick={actions.enableVideo}>
<IoVideocamOffOutline />
</div>
)}
{ state.localAudio && (
<div className="calling-option" onClick={actions.disableAudio}>
<IoMicOutline />
</div>
)}
{ !state.localAudio && (
<div className="calling-option" onClick={actions.enableAudio}>
<IoMicOffOutline />
</div>
)}
{ state.fullscreen && (
<div className="calling-option" onClick={() => actions.setFullscreen(false)}>
<ShrinkOutlined />
</div>
)}
{ !state.fullscreen && (
<div className="calling-option" onClick={() => actions.setFullscreen(true)}>
<ArrowsAltOutlined />
</div>
)}
<div className="calling-end" onClick={actions.end}>
<IoCallOutline />
</div>
</div>
</div> </div>
)}
<div className="calling-options calling-hovered">
{ state.localVideo && (
<div className="calling-option" onClick={actions.disableVideo}>
<IoVideocamOutline />
</div>
)}
{ !state.localVideo && (
<div className="calling-option" onClick={actions.enableVideo}>
<IoVideocamOffOutline />
</div>
)}
{ state.localAudio && (
<div className="calling-option" onClick={actions.disableAudio}>
<IoMicOutline />
</div>
)}
{ !state.localAudio && (
<div className="calling-option" onClick={actions.enableAudio}>
<IoMicOffOutline />
</div>
)}
</div>
<div className="calling-end calling-hovered" onClick={actions.end}>
<IoCallOutline />
</div> </div>
</CallingWrapper> </CallingWrapper>
</Modal> </Modal>

View File

@ -73,66 +73,94 @@ export const RingingWrapper = styled.div`
`; `;
export const CallingWrapper = styled.div` export const CallingWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
&:hover .calling-hovered { .fullscreen {
display: flex; position: fixed;
}
.calling-local {
width: 33%;
bottom: 16px;
left: 16px;
position: absolute;
}
.calling-logo {
position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 256px; width: 100vw;
height: 256px; height: 100vh;
background-color: yellow; background-color: ${props => props.theme.frameArea};
} }
.calling-end { .modal {
position: absolute; background-color: ${props => props.theme.frameArea};
bottom: 16px;
color: ${Colors.white};
font-size: 24px;
background-color: ${Colors.alert};
display: none;
align-items: center;
justify-content: center;
padding: 8px;
border-radius: 20px;
cursor: pointer;
transform: rotate(135deg);
} }
.calling-options { .window {
display: none;
position: absolute;
top: 16px;
flex-direction: row;
}
.calling-option {
color: ${Colors.white};
font-size: 24px;
background-color: ${Colors.primary};
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 8px; position: relative;
border-radius: 20px; width: 100%;
cursor: pointer; height: 100%;
margin-left: 8px;
margin-right: 8px; &:hover .calling-hovered {
} display: flex;
`; }
.logo {
width: 100%;
height: 100%;
border-radius: 4px;
}
.image {
object-fit: contain;
}
.calling-local {
width: 33%;
bottom: 16px;
left: 16px;
position: absolute;
}
.calling-logo {
position: absolute;
top: 0;
left: 0;
width: 256px;
height: 256px;
background-color: yellow;
}
.calling-options {
display: none;
position: absolute;
bottom: 16px;
flex-direction: row;
}
.calling-option {
color: ${Colors.white};
background-color: ${Colors.primary};
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
border-radius: 20px;
cursor: pointer;
margin-left: 8px;
margin-right: 8px;
}
.calling-end {
color: ${Colors.white};
background-color: ${Colors.alert};
transform: rotate(135deg);
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
border-radius: 20px;
cursor: pointer;
margin-left: 8px;
margin-right: 8px;
}
`;
export const SessionWrapper = styled.div` export const SessionWrapper = styled.div`

View File

@ -34,6 +34,7 @@ export function useSession() {
remoteAudio: false, remoteAudio: false,
audioId: null, audioId: null,
videoId: null, videoId: null,
fullscreen: false,
}); });
const app = useContext(AppContext); const app = useContext(AppContext);
@ -78,6 +79,11 @@ export function useSession() {
const { callStatus, localStream, localVideo, localAudio, remoteStream, remoteVideo, remoteAudio } = ring.state; const { callStatus, localStream, localVideo, localAudio, remoteStream, remoteVideo, remoteAudio } = ring.state;
updateState({ ringing, callStatus, callLogo, localStream, localVideo, localAudio, remoteStream, remoteVideo, remoteAudio }); updateState({ ringing, callStatus, callLogo, localStream, localVideo, localAudio, remoteStream, remoteVideo, remoteAudio });
if (!callStatus && state.fullscreen) {
updateState({ fullscreen: false });
}
// eslint-disable-next-line // eslint-disable-next-line
}, [ring.state]); }, [ring.state]);
@ -120,6 +126,9 @@ export function useSession() {
}, [store]); }, [store]);
const actions = { const actions = {
setFullscreen: (fullscreen) => {
updateState({ fullscreen });
},
openCards: () => { openCards: () => {
updateState({ cards: true }); updateState({ cards: true });
}, },