From a0b33fbbbbcfe9acab38d80f46de54cc406eea1b Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 29 Oct 2021 14:15:02 +1100 Subject: [PATCH] add hook to listen for video call events --- ts/components/session/ActionsPanel.tsx | 2 +- ...ntainer.tsx => DraggableCallContainer.tsx} | 31 +++++++++-------- .../calling/InConversationCallContainer.tsx | 33 ++++++++----------- .../session/calling/IncomingCallDialog.tsx | 27 +++++++-------- ts/hooks/useParamSelector.ts | 30 +++++++++++++++++ ts/hooks/useVideoEventListener.ts | 7 ++-- ts/session/utils/CallManager.ts | 2 +- ts/state/selectors/conversations.ts | 20 +++++++++-- 8 files changed, 96 insertions(+), 56 deletions(-) rename ts/components/session/calling/{CallContainer.tsx => DraggableCallContainer.tsx} (86%) create mode 100644 ts/hooks/useParamSelector.ts diff --git a/ts/components/session/ActionsPanel.tsx b/ts/components/session/ActionsPanel.tsx index 612d75909..e7e8d51ce 100644 --- a/ts/components/session/ActionsPanel.tsx +++ b/ts/components/session/ActionsPanel.tsx @@ -46,7 +46,7 @@ import { loadDefaultRooms } from '../../opengroup/opengroupV2/ApiUtil'; import { ActionPanelOnionStatusLight } from '../dialog/OnionStatusPathDialog'; import { switchHtmlToDarkTheme, switchHtmlToLightTheme } from '../../state/ducks/SessionTheme'; -import { DraggableCallContainer } from './calling/CallContainer'; +import { DraggableCallContainer } from './calling/DraggableCallContainer'; import { IncomingCallDialog } from './calling/IncomingCallDialog'; import { CallInFullScreenContainer } from './calling/CallInFullScreenContainer'; diff --git a/ts/components/session/calling/CallContainer.tsx b/ts/components/session/calling/DraggableCallContainer.tsx similarity index 86% rename from ts/components/session/calling/CallContainer.tsx rename to ts/components/session/calling/DraggableCallContainer.tsx index 8f7fabee3..610841251 100644 --- a/ts/components/session/calling/CallContainer.tsx +++ b/ts/components/session/calling/DraggableCallContainer.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; import Draggable, { DraggableData, DraggableEvent } from 'react-draggable'; @@ -11,8 +11,8 @@ import { } from '../../../state/selectors/conversations'; import { openConversationWithMessages } from '../../../state/ducks/conversations'; import { Avatar, AvatarSize } from '../../Avatar'; -import { getConversationController } from '../../../session/conversations'; import { useVideoCallEventsListener } from '../../../hooks/useVideoEventListener'; +import { useAvatarPath, useConversationUsername } from '../../../hooks/useParamSelector'; export const DraggableCallWindow = styled.div` position: absolute; @@ -74,9 +74,12 @@ export const DraggableCallContainer = () => { const ongoingCallPubkey = ongoingCallProps?.id; const { remoteStreamVideoIsMuted, remoteStream } = useVideoCallEventsListener( - 'DraggableCallContainer' + 'DraggableCallContainer', + false ); - const videoRefRemote = useRef(undefined); + const ongoingCallUsername = useConversationUsername(ongoingCallPubkey); + const avatarPath = useAvatarPath(ongoingCallPubkey); + const videoRefRemote = useRef(null); function onWindowResize() { if (positionY + 50 > window.innerHeight || positionX + 50 > window.innerWidth) { @@ -95,24 +98,26 @@ export const DraggableCallContainer = () => { if (videoRefRemote?.current?.srcObject && remoteStream) { videoRefRemote.current.srcObject = remoteStream; + videoRefRemote.current.load(); } - const openCallingConversation = useCallback(() => { + useEffect(() => { + if (videoRefRemote?.current) { + videoRefRemote.current.srcObject = remoteStream; + + videoRefRemote.current.load(); + } + }, [remoteStream, videoRefRemote, videoRefRemote?.current]); + + const openCallingConversation = () => { if (ongoingCallPubkey && ongoingCallPubkey !== selectedConversationKey) { void openConversationWithMessages({ conversationKey: ongoingCallPubkey }); } - }, [ongoingCallPubkey, selectedConversationKey]); + }; if (!hasOngoingCall || !ongoingCallProps || ongoingCallPubkey === selectedConversationKey) { return null; } - const ongoingCallUsername = ongoingCallProps?.profileName || ongoingCallProps?.name; - - const avatarPath = ongoingCallPubkey - ? getConversationController() - .get(ongoingCallPubkey) - .getAvatarPath() - : undefined; return ( { const ongoingCallPubkey = useSelector(getHasOngoingCallWithPubkey); const ongoingCallUsername = ongoingCallProps?.profileName || ongoingCallProps?.name; - const videoRefRemote = useRef(); - const videoRefLocal = useRef(); - - const remoteAvatarPath = ongoingCallPubkey - ? getConversationController() - .get(ongoingCallPubkey) - .getAvatarPath() - : undefined; + const videoRefRemote = useRef(null); + const videoRefLocal = useRef(null); const ourPubkey = UserUtils.getOurPubKeyStrFromCache(); - const ourUsername = getConversationController() - .get(ourPubkey) - .getProfileName(); - const ourAvatarPath = getConversationController() - .get(ourPubkey) - .getAvatarPath(); + const remoteAvatarPath = useAvatarPath(ongoingCallPubkey); + const ourAvatarPath = useOurAvatarPath(); + + const ourUsername = useOurConversationUsername(); const { currentConnectedAudioInputs, @@ -278,7 +271,7 @@ export const InConversationCallContainer = () => { remoteStream, remoteStreamVideoIsMuted, isAudioMuted, - } = useVideoCallEventsListener('InConversationCallContainer'); + } = useVideoCallEventsListener('InConversationCallContainer', true); if (videoRefRemote?.current && videoRefLocal?.current) { videoRefRemote.current.srcObject = remoteStream; diff --git a/ts/components/session/calling/IncomingCallDialog.tsx b/ts/components/session/calling/IncomingCallDialog.tsx index 315c2b47f..6bd119380 100644 --- a/ts/components/session/calling/IncomingCallDialog.tsx +++ b/ts/components/session/calling/IncomingCallDialog.tsx @@ -3,6 +3,7 @@ import { useSelector } from 'react-redux'; import styled from 'styled-components'; import _ from 'underscore'; +import { useAvatarPath, useConversationUsername } from '../../../hooks/useParamSelector'; import { CallManager } from '../../../session/utils'; import { getHasIncomingCall, getHasIncomingCallFrom } from '../../../state/selectors/conversations'; import { Avatar, AvatarSize } from '../../Avatar'; @@ -26,44 +27,38 @@ const IncomingCallAvatatContainer = styled.div` padding: 0 0 2rem 0; `; -// TODO: -/** - * Add mute input, deafen, end call, possibly add person to call - * duration - look at how duration calculated for recording. - */ export const IncomingCallDialog = () => { const hasIncomingCall = useSelector(getHasIncomingCall); - const incomingCallProps = useSelector(getHasIncomingCallFrom); + const incomingCallFromPubkey = useSelector(getHasIncomingCallFrom); //#region input handlers const handleAcceptIncomingCall = async () => { - if (incomingCallProps?.id) { - await CallManager.USER_acceptIncomingCallRequest(incomingCallProps.id); + if (incomingCallFromPubkey) { + await CallManager.USER_acceptIncomingCallRequest(incomingCallFromPubkey); } }; const handleDeclineIncomingCall = async () => { // close the modal - if (incomingCallProps?.id) { - await CallManager.USER_rejectIncomingCallRequest(incomingCallProps.id); + if (incomingCallFromPubkey) { + await CallManager.USER_rejectIncomingCallRequest(incomingCallFromPubkey); } }; - + const from = useConversationUsername(incomingCallFromPubkey); + const incomingAvatar = useAvatarPath(incomingCallFromPubkey); if (!hasIncomingCall) { return null; } - const from = incomingCallProps?.profileName || incomingCallProps?.name || incomingCallProps?.id; - if (hasIncomingCall) { return (
diff --git a/ts/hooks/useParamSelector.ts b/ts/hooks/useParamSelector.ts new file mode 100644 index 000000000..595727dc9 --- /dev/null +++ b/ts/hooks/useParamSelector.ts @@ -0,0 +1,30 @@ +import { useSelector } from 'react-redux'; +import { UserUtils } from '../session/utils'; +import { StateType } from '../state/reducer'; + +export function useAvatarPath(pubkey: string | undefined) { + return useSelector((state: StateType) => { + if (!pubkey) { + return undefined; + } + return state.conversations.conversationLookup[pubkey]?.avatarPath; + }); +} + +export function useOurAvatarPath() { + return useAvatarPath(UserUtils.getOurPubKeyStrFromCache()); +} + +export function useConversationUsername(pubkey: string | undefined) { + return useSelector((state: StateType) => { + if (!pubkey) { + return undefined; + } + const convo = state.conversations.conversationLookup[pubkey]; + return convo?.profileName || convo?.name || convo.id; + }); +} + +export function useOurConversationUsername() { + return useConversationUsername(UserUtils.getOurPubKeyStrFromCache()); +} diff --git a/ts/hooks/useVideoEventListener.ts b/ts/hooks/useVideoEventListener.ts index 61a1476e8..047c93033 100644 --- a/ts/hooks/useVideoEventListener.ts +++ b/ts/hooks/useVideoEventListener.ts @@ -10,7 +10,7 @@ import { getSelectedConversationKey, } from '../state/selectors/conversations'; -export function useVideoCallEventsListener(uniqueId: string) { +export function useVideoCallEventsListener(uniqueId: string, onSame: boolean) { const selectedConversationKey = useSelector(getSelectedConversationKey); const ongoingCallPubkey = useSelector(getHasOngoingCallWithPubkey); const isFullScreen = useSelector(getCallIsInFullScreen); @@ -27,7 +27,10 @@ export function useVideoCallEventsListener(uniqueId: string) { [] ); useEffect(() => { - if (ongoingCallPubkey === selectedConversationKey) { + if ( + (onSame && ongoingCallPubkey === selectedConversationKey) || + (!onSame && ongoingCallPubkey !== selectedConversationKey) + ) { CallManager.addVideoEventsListener(uniqueId, (options: CallManagerOptionsType) => { const { audioInputsList, diff --git a/ts/session/utils/CallManager.ts b/ts/session/utils/CallManager.ts index 1f2295aa4..12504865f 100644 --- a/ts/session/utils/CallManager.ts +++ b/ts/session/utils/CallManager.ts @@ -459,10 +459,10 @@ function onDataChannelReceivedMessage(ev: MessageEvent) { if (parsed.video !== undefined) { remoteVideoStreamIsMuted = !Boolean(parsed.video); } - callVideoListeners(); } catch (e) { window.log.warn('onDataChannelReceivedMessage Could not parse data in event', ev); } + callVideoListeners(); } function onDataChannelOnOpen() { window.log.info('onDataChannelOnOpen: sending video status'); diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index e6384037d..e3e276cd3 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -49,6 +49,7 @@ export const getConversationLookup = createSelector( export const getConversationsCount = createSelector(getConversationLookup, (state): number => { return Object.values(state).length; }); + export const getBlockedPubkeys = createSelector( // make sure to extends this selector to we are rerun on conversation changes getConversationLookup, @@ -81,9 +82,22 @@ export const getSelectedConversationIsPublic = createSelector( } ); +const getConversationId = (_whatever: any, id: string) => id; + +export const getConversationById = createSelector( + getConversations, + getConversationId, + ( + state: ConversationsStateType, + convoId: string | undefined + ): ReduxConversationType | undefined => { + return convoId ? state.conversationLookup[convoId] : undefined; + } +); + export const getHasIncomingCallFrom = createSelector( getConversations, - (state: ConversationsStateType): ReduxConversationType | undefined => { + (state: ConversationsStateType): string | undefined => { const foundEntry = Object.entries(state.conversationLookup).find( ([_convoKey, convo]) => convo.callState === 'incoming' ); @@ -91,7 +105,7 @@ export const getHasIncomingCallFrom = createSelector( if (!foundEntry) { return undefined; } - return foundEntry[1]; + return foundEntry[1].id; } ); @@ -114,7 +128,7 @@ export const getHasOngoingCallWith = createSelector( export const getHasIncomingCall = createSelector( getHasIncomingCallFrom, - (withConvo: ReduxConversationType | undefined): boolean => !!withConvo + (withConvo: string | undefined): boolean => !!withConvo ); export const getHasOngoingCall = createSelector(