diff --git a/packages/desktop/src/providers/ConversationProvider.tsx b/packages/desktop/src/providers/ConversationProvider.tsx index edfb95f..62bc8c5 100644 --- a/packages/desktop/src/providers/ConversationProvider.tsx +++ b/packages/desktop/src/providers/ConversationProvider.tsx @@ -63,6 +63,12 @@ function useSocketFire() { return { handleConnectToLine, handleTuneIntoLine, handleUntuneFromLine }; } +const videoConstraints = { + frameRate: 15, + width: { max: 720, ideal: 720, min: 100 }, + height: { max: 720, ideal: 720, min: 100 }, +}; + interface IConversationContext { conversationMap: ConversationMap; @@ -78,6 +84,10 @@ interface IConversationContext { handleEscape?: () => void; updateConversationPriority?: (conversationId: string, newState: MemberState) => Promise; + + userLocalStream?: MediaStream; + handleCastVideo?: () => void; + handleStopVideo?: () => void; } const ConversationContext = React.createContext({ @@ -412,6 +422,7 @@ export function ConversationProvider({ children }: { children: React.ReactNode } return [priorityConversations, inboxConversations]; }, [conversationMap, user]); + // move from priority to inbox and vice versa const updateConversationPriorityHandler = useCallback( async (conversationId: string, newState: MemberState) => { try { @@ -435,6 +446,22 @@ export function ConversationProvider({ children }: { children: React.ReactNode } [setConversationMap, user], ); + const [userLocalStream, setUserLocalStream] = useState(); + const handleCastVideo = useCallback(() => { + // create a new stream + // if there's a selected conversation, then send it to that conversation + navigator.mediaDevices + .getUserMedia({ video: videoConstraints, audio: false }) + .then((userStream: MediaStream) => { + setUserLocalStream(userStream); + }) + .catch((error) => toast.error('problem getting video')); + }, [setUserLocalStream]); + + const handleStopVideo = useCallback(() => { + setUserLocalStream(undefined); + }, [setUserLocalStream]); + const handleEscape = useCallback(() => { selectConversation(undefined); }, [selectConversation]); @@ -473,6 +500,9 @@ export function ConversationProvider({ children }: { children: React.ReactNode } inboxConversations: masterConversations[1], handleEscape, updateConversationPriority: updateConversationPriorityHandler, + userLocalStream, + handleStopVideo, + handleCastVideo, }} > {children} @@ -493,12 +523,6 @@ export default function useConversations() { return React.useContext(ConversationContext); } -const videoConstraints = { - frameRate: 15, - width: { max: 100 }, - height: { max: 200 }, -}; - const iceServers = [ // { urls: 'stun:stun.l.google.com:19302' }, // { urls: 'stun:stun.l.google.com:19302' }, diff --git a/packages/desktop/src/tree/FooterControls.tsx b/packages/desktop/src/tree/FooterControls.tsx index 4d74707..42a63a7 100644 --- a/packages/desktop/src/tree/FooterControls.tsx +++ b/packages/desktop/src/tree/FooterControls.tsx @@ -30,7 +30,7 @@ import { FiZap, FiZapOff, } from 'react-icons/fi'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { MasterConversation } from '../util/types'; import { MdOutlineRecordVoiceOver } from 'react-icons/md'; @@ -46,7 +46,13 @@ import useZen from '../providers/ZenProvider'; export default function FooterControls() { const { handleFlowState } = useZen(); - const { selectedConversation, priorityConversations } = useConversations(); + const { + selectedConversation, + priorityConversations, + userLocalStream, + handleCastVideo, + handleStopVideo, + } = useConversations(); const { user, handleLogout } = useAuth(); const { handleToggleDesktopMode, desktopMode } = useElectron(); @@ -77,8 +83,16 @@ export default function FooterControls() { const [isVideoOn, setIsVideoOn] = useState(false); const handleToggleVideo = useCallback(() => { - setIsVideoOn((prevVal) => !prevVal); - }, [setIsVideoOn]); + setIsVideoOn((prevVal) => { + if (!prevVal) { + handleCastVideo(); + } else { + handleStopVideo(); + } + + return !prevVal; + }); + }, [setIsVideoOn, handleCastVideo, handleStopVideo]); const handleCloseUserSettings = useCallback(() => { setOpenUserSettings(false); @@ -96,6 +110,13 @@ export default function FooterControls() { return false; }, [selectedConversation, priorityConversations]); + const localVideoRef = useRef(null); + useEffect(() => { + if (localVideoRef?.current && userLocalStream) { + localVideoRef.current.srcObject = userLocalStream; + } + }, [localVideoRef, userLocalStream]); + return ( - + {isVideoOn ? ( + + ) : ( + + )} @@ -188,7 +213,8 @@ function OverlayConversation({ }) { const { user } = useAuth(); - const { selectConversation, handleEscape, updateConversationPriority } = useConversations(); + const { selectConversation, handleEscape, updateConversationPriority, userLocalStream } = + useConversations(); const handleSelectConversation = useCallback(() => { if (!isSelected) { @@ -215,6 +241,10 @@ function OverlayConversation({ updateConversationPriority(masterConversation._id.toString(), 'priority'); }, [updateConversationPriority, isPriority, masterConversation._id]); + const userVideoRef = useRef(null); + + useEffect(() => {}, []); + return ( - {masterConversation.members?.map( - (conversationUser, index) => + {masterConversation.members?.map((conversationUser, index) => { + return ( (conversationUser._id.toString() !== user._id.toString() || isSelected) && ( - ), - )} + ) + ); + })} {isSelected && (