diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 8b649412b..3af377b39 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -443,5 +443,9 @@ "messageDeletedPlaceholder": "This message has been deleted", "messageDeleted": "Message deleted", "surveyTitle": "Take our Session Survey", - "goToOurSurvey": "Go to our survey" + "goToOurSurvey": "Go to our survey", + "incomingCall": "Incoming call", + "accept": "Accept", + "decline": "Decline", + "endCall": "End call" } diff --git a/package.json b/package.json index 7eac174a1..2a7359247 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "react": "^17.0.2", "react-contexify": "5.0.0", "react-dom": "^17.0.2", + "react-draggable": "^4.4.4", "react-emoji": "^0.5.0", "react-emoji-render": "^1.2.4", "react-h5-audio-player": "^3.2.0", diff --git a/ts/components/session/calling/CallContainer.tsx b/ts/components/session/calling/CallContainer.tsx index ef1a51781..b947af116 100644 --- a/ts/components/session/calling/CallContainer.tsx +++ b/ts/components/session/calling/CallContainer.tsx @@ -1,5 +1,9 @@ -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import { useSelector } from 'react-redux'; +import Draggable from 'react-draggable'; + +// tslint:disable-next-line: no-submodule-imports +import useMountedState from 'react-use/lib/useMountedState'; import styled from 'styled-components'; import _ from 'underscore'; import { CallManager } from '../../../session/utils'; @@ -15,12 +19,14 @@ import { SessionWrapperModal } from '../SessionWrapperModal'; export const CallWindow = styled.div` position: absolute; z-index: 9; - padding: 2rem; + padding: 1rem; top: 50vh; left: 50vw; transform: translate(-50%, -50%); display: flex; flex-direction: column; + background-color: var(--color-modal-background); + border: var(--session-border); `; // similar styling to modal header @@ -28,7 +34,7 @@ const CallWindowHeader = styled.div` display: flex; flex-direction: row; justify-content: space-between; - align-items: center; + align-self: center; padding: $session-margin-lg; @@ -39,30 +45,29 @@ const CallWindowHeader = styled.div` font-weight: 700; `; -// TODO: Add proper styling for this -const VideoContainer = styled.video` - width: 200px; - height: 200px; +const VideoContainer = styled.div` + position: relative; + max-height: 60vh; +`; + +const VideoContainerRemote = styled.video` + max-height: inherit; +`; +const VideoContainerLocal = styled.video` + max-height: 45%; + max-width: 45%; + position: absolute; + bottom: 0; + right: 0; `; const CallWindowInner = styled.div` - position: relative; - background-color: pink; - border: 1px solid #d3d3d3; text-align: center; - padding: 2rem; - display: flex; - flex-direction: column; + padding: 1rem; `; const CallWindowControls = styled.div` - position: absolute; - top: 100%; - left: 0; - width: 100%; - /* background: green; */ padding: 5px; - transform: translateY(-100%); `; // TODO: @@ -77,6 +82,24 @@ export const CallContainer = () => { const hasOngoingCall = useSelector(getHasOngoingCall); const ongoingOrIncomingPubkey = ongoingCallProps?.id || incomingCallProps?.id; + const videoRefRemote = useRef(); + const videoRefLocal = useRef(); + const mountedState = useMountedState(); + + useEffect(() => { + CallManager.setVideoEventsListener( + (localStream: MediaStream | null, remoteStream: MediaStream | null) => { + if (mountedState() && videoRefRemote?.current && videoRefLocal?.current) { + videoRefLocal.current.srcObject = localStream; + videoRefRemote.current.srcObject = remoteStream; + } + } + ); + + return () => { + CallManager.setVideoEventsListener(null); + }; + }, []); //#region input handlers const handleAcceptIncomingCall = async () => { @@ -107,26 +130,31 @@ export const CallContainer = () => { if (hasOngoingCall && ongoingCallProps) { return ( - - - {ongoingCallProps.name} - - + + + Call with: {ongoingCallProps.name} + + + + + + + - + - - + + ); } if (hasIncomingCall) { return ( - +
- + diff --git a/ts/session/utils/CallManager.ts b/ts/session/utils/CallManager.ts index 28b737947..cc6621db3 100644 --- a/ts/session/utils/CallManager.ts +++ b/ts/session/utils/CallManager.ts @@ -12,6 +12,15 @@ import { ed25519Str } from '../onions/onionPath'; import { getMessageQueue } from '../sending'; import { PubKey } from '../types'; +type CallManagerListener = + | ((localStream: MediaStream | null, remoteStream: MediaStream | null) => void) + | null; +let videoEventsListener: CallManagerListener; + +export function setVideoEventsListener(listener: CallManagerListener) { + videoEventsListener = listener; +} + /** * This field stores all the details received by a sender about a call in separate messages. */ @@ -67,17 +76,15 @@ export async function USER_callRecipient(recipient: string) { void iceSenderDebouncer(recipient); } }); + const remoteStream = new MediaStream(); - const localVideo = document.querySelector('#video-local') as any; - if (localVideo) { - localVideo.srcObject = mediaDevices; + if (videoEventsListener) { + videoEventsListener(mediaDevices, remoteStream); } - const remoteStream = new MediaStream(); peerConnection.addEventListener('track', event => { - const remoteVideo = document.querySelector('#video-remote') as any; - if (remoteVideo) { - remoteVideo.srcObject = remoteStream; + if (videoEventsListener) { + videoEventsListener(mediaDevices, remoteStream); } remoteStream.addTrack(event.track); }); @@ -190,18 +197,15 @@ export async function USER_acceptIncomingCallRequest(fromSender: string) { // window.log.info('USER_acceptIncomingCallRequest adding track ', track); peerConnection?.addTrack(track, mediaDevices); }); + const remoteStream = new MediaStream(); - const localVideo = document.querySelector('#video-local') as any; - if (localVideo) { - localVideo.srcObject = mediaDevices; + if (videoEventsListener) { + videoEventsListener(mediaDevices, remoteStream); } - const remoteStream = new MediaStream(); - peerConnection.addEventListener('track', event => { - const remoteVideo = document.querySelector('#video-remote') as any; - if (remoteVideo) { - remoteVideo.srcObject = remoteStream; + if (videoEventsListener) { + videoEventsListener(mediaDevices, remoteStream); } remoteStream.addTrack(event.track); }); @@ -280,13 +284,8 @@ export async function USER_rejectIncomingCallRequest(fromSender: string) { export function handleEndCallMessage(sender: string) { callCache.delete(sender); - const remoteVideo = document.querySelector('#video-remote') as any; - if (remoteVideo) { - remoteVideo.srcObject = null; - } - const localVideo = document.querySelector('#video-local') as any; - if (localVideo) { - localVideo.srcObject = null; + if (videoEventsListener) { + videoEventsListener(null, null); } // // FIXME audric trigger UI cleanup diff --git a/yarn.lock b/yarn.lock index 95d56a27e..9b47919d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7237,6 +7237,14 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-draggable@^4.4.4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.4.tgz#5b26d9996be63d32d285a426f41055de87e59b2f" + integrity sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA== + dependencies: + clsx "^1.1.1" + prop-types "^15.6.0" + react-emoji-render@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/react-emoji-render/-/react-emoji-render-1.2.4.tgz#fa3542a692e1eed3236f0f12b8e3a61b2818e2c2"