Redux state: Allow multiple calls to be stored
This commit is contained in:
parent
753e0279c6
commit
3468de255d
21 changed files with 1191 additions and 515 deletions
|
@ -13,11 +13,7 @@ import enMessages from '../../_locales/en/messages.json';
|
|||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const callDetails = {
|
||||
callId: 0,
|
||||
isIncoming: true,
|
||||
isVideoCall: true,
|
||||
|
||||
const conversation = {
|
||||
id: '3051234567',
|
||||
avatarPath: undefined,
|
||||
color: 'ultramarine' as ColorType,
|
||||
|
@ -30,27 +26,20 @@ const callDetails = {
|
|||
const defaultProps = {
|
||||
availableCameras: [],
|
||||
acceptCall: action('accept-call'),
|
||||
callDetails,
|
||||
callState: CallState.Accepted,
|
||||
cancelCall: action('cancel-call'),
|
||||
closeNeedPermissionScreen: action('close-need-permission-screen'),
|
||||
declineCall: action('decline-call'),
|
||||
hangUp: action('hang-up'),
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: true,
|
||||
hasRemoteVideo: true,
|
||||
i18n,
|
||||
me: {
|
||||
color: 'ultramarine' as ColorType,
|
||||
title: 'Morty Smith',
|
||||
},
|
||||
pip: false,
|
||||
renderDeviceSelection: () => <div />,
|
||||
setLocalAudio: action('set-local-audio'),
|
||||
setLocalPreview: action('set-local-preview'),
|
||||
setLocalVideo: action('set-local-video'),
|
||||
setRendererCanvas: action('set-renderer-canvas'),
|
||||
settingsDialogOpen: false,
|
||||
startCall: action('start-call'),
|
||||
toggleParticipants: action('toggle-participants'),
|
||||
togglePip: action('toggle-pip'),
|
||||
|
@ -59,20 +48,71 @@ const defaultProps = {
|
|||
|
||||
const permutations = [
|
||||
{
|
||||
title: 'Call Manager (ongoing)',
|
||||
title: 'Call Manager (no call)',
|
||||
props: {},
|
||||
},
|
||||
{
|
||||
title: 'Call Manager (ongoing)',
|
||||
props: {
|
||||
activeCall: {
|
||||
call: {
|
||||
conversationId: '3051234567',
|
||||
callState: CallState.Accepted,
|
||||
isIncoming: false,
|
||||
isVideoCall: true,
|
||||
hasRemoteVideo: true,
|
||||
},
|
||||
activeCallState: {
|
||||
conversationId: '3051234567',
|
||||
joinedAt: Date.now(),
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
participantsList: false,
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
},
|
||||
conversation,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Call Manager (ringing)',
|
||||
props: {
|
||||
callState: CallState.Ringing,
|
||||
incomingCall: {
|
||||
call: {
|
||||
conversationId: '3051234567',
|
||||
callState: CallState.Ringing,
|
||||
isIncoming: true,
|
||||
isVideoCall: true,
|
||||
hasRemoteVideo: true,
|
||||
},
|
||||
conversation,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Call Manager (call request needed)',
|
||||
props: {
|
||||
callState: CallState.Ended,
|
||||
callEndedReason: CallEndedReason.RemoteHangupNeedPermission,
|
||||
activeCall: {
|
||||
call: {
|
||||
conversationId: '3051234567',
|
||||
callState: CallState.Ended,
|
||||
callEndedReason: CallEndedReason.RemoteHangupNeedPermission,
|
||||
isIncoming: false,
|
||||
isVideoCall: true,
|
||||
hasRemoteVideo: true,
|
||||
},
|
||||
activeCallState: {
|
||||
conversationId: '3051234567',
|
||||
joinedAt: Date.now(),
|
||||
hasLocalAudio: true,
|
||||
hasLocalVideo: false,
|
||||
participantsList: false,
|
||||
pip: false,
|
||||
settingsDialogOpen: false,
|
||||
},
|
||||
conversation,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -5,112 +5,156 @@ import React from 'react';
|
|||
import { CallingPip } from './CallingPip';
|
||||
import { CallNeedPermissionScreen } from './CallNeedPermissionScreen';
|
||||
import { CallingLobby } from './CallingLobby';
|
||||
import { CallScreen, PropsType as CallScreenPropsType } from './CallScreen';
|
||||
import {
|
||||
IncomingCallBar,
|
||||
PropsType as IncomingCallBarPropsType,
|
||||
} from './IncomingCallBar';
|
||||
import { CallScreen } from './CallScreen';
|
||||
import { IncomingCallBar } from './IncomingCallBar';
|
||||
import { CallState, CallEndedReason } from '../types/Calling';
|
||||
import { CallDetailsType, OutgoingCallType } from '../state/ducks/calling';
|
||||
import {
|
||||
ActiveCallStateType,
|
||||
AcceptCallType,
|
||||
DeclineCallType,
|
||||
DirectCallStateType,
|
||||
StartCallType,
|
||||
SetLocalAudioType,
|
||||
HangUpType,
|
||||
SetLocalPreviewType,
|
||||
SetLocalVideoType,
|
||||
SetRendererCanvasType,
|
||||
} from '../state/ducks/calling';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { ColorType } from '../types/Colors';
|
||||
|
||||
type CallManagerPropsType = {
|
||||
interface PropsType {
|
||||
activeCall?: {
|
||||
call: DirectCallStateType;
|
||||
activeCallState: ActiveCallStateType;
|
||||
conversation: {
|
||||
id: string;
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
title: string;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
};
|
||||
};
|
||||
availableCameras: Array<MediaDeviceInfo>;
|
||||
callDetails?: CallDetailsType;
|
||||
callEndedReason?: CallEndedReason;
|
||||
callState?: CallState;
|
||||
cancelCall: () => void;
|
||||
pip: boolean;
|
||||
closeNeedPermissionScreen: () => void;
|
||||
incomingCall?: {
|
||||
call: DirectCallStateType;
|
||||
conversation: {
|
||||
id: string;
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
title: string;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
};
|
||||
};
|
||||
renderDeviceSelection: () => JSX.Element;
|
||||
settingsDialogOpen: boolean;
|
||||
startCall: (payload: OutgoingCallType) => void;
|
||||
startCall: (payload: StartCallType) => void;
|
||||
toggleParticipants: () => void;
|
||||
};
|
||||
|
||||
type PropsType = IncomingCallBarPropsType &
|
||||
CallScreenPropsType &
|
||||
CallManagerPropsType;
|
||||
acceptCall: (_: AcceptCallType) => void;
|
||||
declineCall: (_: DeclineCallType) => void;
|
||||
i18n: LocalizerType;
|
||||
me: {
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
};
|
||||
setLocalAudio: (_: SetLocalAudioType) => void;
|
||||
setLocalVideo: (_: SetLocalVideoType) => void;
|
||||
setLocalPreview: (_: SetLocalPreviewType) => void;
|
||||
setRendererCanvas: (_: SetRendererCanvasType) => void;
|
||||
hangUp: (_: HangUpType) => void;
|
||||
togglePip: () => void;
|
||||
toggleSettings: () => void;
|
||||
}
|
||||
|
||||
export const CallManager = ({
|
||||
acceptCall,
|
||||
activeCall,
|
||||
availableCameras,
|
||||
callDetails,
|
||||
callState,
|
||||
callEndedReason,
|
||||
cancelCall,
|
||||
closeNeedPermissionScreen,
|
||||
declineCall,
|
||||
hangUp,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
hasRemoteVideo,
|
||||
i18n,
|
||||
incomingCall,
|
||||
me,
|
||||
pip,
|
||||
renderDeviceSelection,
|
||||
setLocalAudio,
|
||||
setLocalPreview,
|
||||
setLocalVideo,
|
||||
setRendererCanvas,
|
||||
settingsDialogOpen,
|
||||
startCall,
|
||||
toggleParticipants,
|
||||
togglePip,
|
||||
toggleSettings,
|
||||
}: PropsType): JSX.Element | null => {
|
||||
if (!callDetails) {
|
||||
return null;
|
||||
}
|
||||
const incoming = callDetails.isIncoming;
|
||||
const outgoing = !incoming;
|
||||
const ongoing =
|
||||
callState === CallState.Accepted || callState === CallState.Reconnecting;
|
||||
const ringing = callState === CallState.Ringing;
|
||||
const ended = callState === CallState.Ended;
|
||||
if (activeCall) {
|
||||
const { call, activeCallState, conversation } = activeCall;
|
||||
const { callState, callEndedReason } = call;
|
||||
const {
|
||||
joinedAt,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
settingsDialogOpen,
|
||||
pip,
|
||||
} = activeCallState;
|
||||
|
||||
if (ended) {
|
||||
if (callEndedReason === CallEndedReason.RemoteHangupNeedPermission) {
|
||||
const ended = callState === CallState.Ended;
|
||||
if (ended) {
|
||||
if (callEndedReason === CallEndedReason.RemoteHangupNeedPermission) {
|
||||
return (
|
||||
<CallNeedPermissionScreen
|
||||
close={closeNeedPermissionScreen}
|
||||
conversation={conversation}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!callState) {
|
||||
return (
|
||||
<CallNeedPermissionScreen
|
||||
close={closeNeedPermissionScreen}
|
||||
callDetails={callDetails}
|
||||
i18n={i18n}
|
||||
/>
|
||||
<>
|
||||
<CallingLobby
|
||||
availableCameras={availableCameras}
|
||||
conversation={conversation}
|
||||
hasLocalAudio={hasLocalAudio}
|
||||
hasLocalVideo={hasLocalVideo}
|
||||
i18n={i18n}
|
||||
isGroupCall={false}
|
||||
onCallCanceled={cancelCall}
|
||||
onJoinCall={() => {
|
||||
startCall({
|
||||
conversationId: conversation.id,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
});
|
||||
}}
|
||||
setLocalPreview={setLocalPreview}
|
||||
setLocalAudio={setLocalAudio}
|
||||
setLocalVideo={setLocalVideo}
|
||||
toggleParticipants={toggleParticipants}
|
||||
toggleSettings={toggleSettings}
|
||||
/>
|
||||
{settingsDialogOpen && renderDeviceSelection()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!callState) {
|
||||
return (
|
||||
<>
|
||||
<CallingLobby
|
||||
availableCameras={availableCameras}
|
||||
callDetails={callDetails}
|
||||
hasLocalAudio={hasLocalAudio}
|
||||
hasLocalVideo={hasLocalVideo}
|
||||
i18n={i18n}
|
||||
isGroupCall={false}
|
||||
onCallCanceled={cancelCall}
|
||||
onJoinCall={() => {
|
||||
startCall({ callDetails });
|
||||
}}
|
||||
setLocalPreview={setLocalPreview}
|
||||
setLocalAudio={setLocalAudio}
|
||||
setLocalVideo={setLocalVideo}
|
||||
toggleParticipants={toggleParticipants}
|
||||
toggleSettings={toggleSettings}
|
||||
/>
|
||||
{settingsDialogOpen && renderDeviceSelection()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
const hasRemoteVideo = Boolean(call.hasRemoteVideo);
|
||||
|
||||
if (outgoing || ongoing) {
|
||||
if (pip) {
|
||||
return (
|
||||
<CallingPip
|
||||
callDetails={callDetails}
|
||||
conversation={conversation}
|
||||
hangUp={hangUp}
|
||||
hasLocalVideo={hasLocalVideo}
|
||||
hasRemoteVideo={hasRemoteVideo}
|
||||
|
@ -125,12 +169,13 @@ export const CallManager = ({
|
|||
return (
|
||||
<>
|
||||
<CallScreen
|
||||
callDetails={callDetails}
|
||||
conversation={conversation}
|
||||
callState={callState}
|
||||
hangUp={hangUp}
|
||||
hasLocalAudio={hasLocalAudio}
|
||||
hasLocalVideo={hasLocalVideo}
|
||||
i18n={i18n}
|
||||
joinedAt={joinedAt}
|
||||
me={me}
|
||||
hasRemoteVideo={hasRemoteVideo}
|
||||
setLocalPreview={setLocalPreview}
|
||||
|
@ -145,17 +190,18 @@ export const CallManager = ({
|
|||
);
|
||||
}
|
||||
|
||||
if (incoming && ringing) {
|
||||
// In the future, we may want to show the incoming call bar when a call is active.
|
||||
if (incomingCall) {
|
||||
return (
|
||||
<IncomingCallBar
|
||||
acceptCall={acceptCall}
|
||||
callDetails={callDetails}
|
||||
declineCall={declineCall}
|
||||
i18n={i18n}
|
||||
call={incomingCall.call}
|
||||
conversation={incomingCall.conversation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Incoming && Prering
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -2,14 +2,21 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { CallDetailsType } from '../state/ducks/calling';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { Avatar } from './Avatar';
|
||||
import { Intl } from './Intl';
|
||||
import { ContactName } from './conversation/ContactName';
|
||||
import { ColorType } from '../types/Colors';
|
||||
|
||||
interface Props {
|
||||
callDetails: CallDetailsType;
|
||||
conversation: {
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
title: string;
|
||||
};
|
||||
i18n: LocalizerType;
|
||||
close: () => void;
|
||||
}
|
||||
|
@ -17,11 +24,11 @@ interface Props {
|
|||
const AUTO_CLOSE_MS = 10000;
|
||||
|
||||
export const CallNeedPermissionScreen: React.FC<Props> = ({
|
||||
callDetails,
|
||||
conversation,
|
||||
i18n,
|
||||
close,
|
||||
}) => {
|
||||
const title = callDetails.title || i18n('unknownContact');
|
||||
const title = conversation.title || i18n('unknownContact');
|
||||
|
||||
const autoCloseAtRef = useRef<number>(Date.now() + AUTO_CLOSE_MS);
|
||||
useEffect(() => {
|
||||
|
@ -32,15 +39,15 @@ export const CallNeedPermissionScreen: React.FC<Props> = ({
|
|||
return (
|
||||
<div className="module-call-need-permission-screen">
|
||||
<Avatar
|
||||
avatarPath={callDetails.avatarPath}
|
||||
color={callDetails.color || 'ultramarine'}
|
||||
avatarPath={conversation.avatarPath}
|
||||
color={conversation.color || 'ultramarine'}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
name={callDetails.name}
|
||||
phoneNumber={callDetails.phoneNumber}
|
||||
profileName={callDetails.profileName}
|
||||
title={callDetails.title}
|
||||
name={conversation.name}
|
||||
phoneNumber={conversation.phoneNumber}
|
||||
profileName={conversation.profileName}
|
||||
title={conversation.title}
|
||||
size={112}
|
||||
/>
|
||||
|
||||
|
|
|
@ -14,23 +14,16 @@ import enMessages from '../../_locales/en/messages.json';
|
|||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const callDetails = {
|
||||
acceptedTime: Date.now(),
|
||||
callId: 0,
|
||||
isIncoming: true,
|
||||
isVideoCall: true,
|
||||
|
||||
id: '3051234567',
|
||||
avatarPath: undefined,
|
||||
color: 'ultramarine' as ColorType,
|
||||
title: 'Rick Sanchez',
|
||||
name: 'Rick Sanchez',
|
||||
phoneNumber: '3051234567',
|
||||
profileName: 'Rick Sanchez',
|
||||
};
|
||||
|
||||
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||
callDetails,
|
||||
conversation: {
|
||||
id: '3051234567',
|
||||
avatarPath: undefined,
|
||||
color: 'ultramarine' as ColorType,
|
||||
title: 'Rick Sanchez',
|
||||
name: 'Rick Sanchez',
|
||||
phoneNumber: '3051234567',
|
||||
profileName: 'Rick Sanchez',
|
||||
},
|
||||
callState: select(
|
||||
'callState',
|
||||
CallState,
|
||||
|
@ -44,6 +37,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
|||
overrideProps.hasRemoteVideo || false
|
||||
),
|
||||
i18n,
|
||||
joinedAt: Date.now(),
|
||||
me: {
|
||||
color: 'ultramarine' as ColorType,
|
||||
name: 'Morty Smith',
|
||||
|
|
|
@ -5,7 +5,6 @@ import React, { useState, useRef, useEffect, useCallback } from 'react';
|
|||
import { noop } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
CallDetailsType,
|
||||
HangUpType,
|
||||
SetLocalAudioType,
|
||||
SetLocalPreviewType,
|
||||
|
@ -20,13 +19,22 @@ import { ColorType } from '../types/Colors';
|
|||
import { LocalizerType } from '../types/Util';
|
||||
|
||||
export type PropsType = {
|
||||
callDetails?: CallDetailsType;
|
||||
callState?: CallState;
|
||||
conversation: {
|
||||
id: string;
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
title: string;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
};
|
||||
callState: CallState;
|
||||
hangUp: (_: HangUpType) => void;
|
||||
hasLocalAudio: boolean;
|
||||
hasLocalVideo: boolean;
|
||||
hasRemoteVideo: boolean;
|
||||
i18n: LocalizerType;
|
||||
joinedAt?: number;
|
||||
me: {
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
|
@ -44,13 +52,14 @@ export type PropsType = {
|
|||
};
|
||||
|
||||
export const CallScreen: React.FC<PropsType> = ({
|
||||
callDetails,
|
||||
callState,
|
||||
conversation,
|
||||
hangUp,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
hasRemoteVideo,
|
||||
i18n,
|
||||
joinedAt,
|
||||
me,
|
||||
setLocalAudio,
|
||||
setLocalVideo,
|
||||
|
@ -59,29 +68,17 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
togglePip,
|
||||
toggleSettings,
|
||||
}) => {
|
||||
const { acceptedTime, callId } = callDetails || {};
|
||||
|
||||
const toggleAudio = useCallback(() => {
|
||||
if (!callId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLocalAudio({
|
||||
callId,
|
||||
enabled: !hasLocalAudio,
|
||||
});
|
||||
}, [callId, setLocalAudio, hasLocalAudio]);
|
||||
}, [setLocalAudio, hasLocalAudio]);
|
||||
|
||||
const toggleVideo = useCallback(() => {
|
||||
if (!callId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLocalVideo({
|
||||
callId,
|
||||
enabled: !hasLocalVideo,
|
||||
});
|
||||
}, [callId, setLocalVideo, hasLocalVideo]);
|
||||
}, [setLocalVideo, hasLocalVideo]);
|
||||
|
||||
const [acceptedDuration, setAcceptedDuration] = useState<number | null>(null);
|
||||
const [showControls, setShowControls] = useState(true);
|
||||
|
@ -100,15 +97,15 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
}, [setLocalPreview, setRendererCanvas]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!acceptedTime) {
|
||||
if (!joinedAt) {
|
||||
return noop;
|
||||
}
|
||||
// It's really jumpy with a value of 500ms.
|
||||
const interval = setInterval(() => {
|
||||
setAcceptedDuration(Date.now() - acceptedTime);
|
||||
setAcceptedDuration(Date.now() - joinedAt);
|
||||
}, 100);
|
||||
return clearInterval.bind(null, interval);
|
||||
}, [acceptedTime]);
|
||||
}, [joinedAt]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showControls) {
|
||||
|
@ -147,10 +144,6 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
|
||||
const isAudioOnly = !hasLocalVideo && !hasRemoteVideo;
|
||||
|
||||
if (!callDetails || !callState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const controlsFadeClass = classNames({
|
||||
'module-ongoing-call__controls--fadeIn':
|
||||
(showControls || isAudioOnly) && callState !== CallState.Accepted,
|
||||
|
@ -181,7 +174,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
)}
|
||||
>
|
||||
<div className="module-calling__header--header-name">
|
||||
{callDetails.title}
|
||||
{conversation.title}
|
||||
</div>
|
||||
{renderHeaderMessage(i18n, callState, acceptedDuration)}
|
||||
<div className="module-calling-tools">
|
||||
|
@ -205,7 +198,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
ref={remoteVideoRef}
|
||||
/>
|
||||
) : (
|
||||
renderAvatar(i18n, callDetails)
|
||||
renderAvatar(i18n, conversation)
|
||||
)}
|
||||
<div className="module-ongoing-call__footer">
|
||||
{/* This layout-only element is not ideal.
|
||||
|
@ -233,7 +226,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
buttonType={CallingButtonType.HANG_UP}
|
||||
i18n={i18n}
|
||||
onClick={() => {
|
||||
hangUp({ callId });
|
||||
hangUp({ conversationId: conversation.id });
|
||||
}}
|
||||
tooltipDistance={24}
|
||||
/>
|
||||
|
@ -269,16 +262,22 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
|
||||
function renderAvatar(
|
||||
i18n: LocalizerType,
|
||||
callDetails: CallDetailsType
|
||||
): JSX.Element {
|
||||
const {
|
||||
{
|
||||
avatarPath,
|
||||
color,
|
||||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
} = callDetails;
|
||||
}: {
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
title: string;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
}
|
||||
): JSX.Element {
|
||||
return (
|
||||
<div className="module-ongoing-call__remote-video-disabled">
|
||||
<Avatar
|
||||
|
|
|
@ -13,11 +13,7 @@ import enMessages from '../../_locales/en/messages.json';
|
|||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const callDetails = {
|
||||
callId: 0,
|
||||
isIncoming: true,
|
||||
isVideoCall: true,
|
||||
|
||||
const conversation = {
|
||||
id: '3051234567',
|
||||
avatarPath: undefined,
|
||||
color: 'ultramarine' as ColorType,
|
||||
|
@ -39,7 +35,7 @@ const camera = {
|
|||
|
||||
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||
availableCameras: overrideProps.availableCameras || [camera],
|
||||
callDetails,
|
||||
conversation,
|
||||
hasLocalAudio: boolean('hasLocalAudio', overrideProps.hasLocalAudio || false),
|
||||
hasLocalVideo: boolean('hasLocalVideo', overrideProps.hasLocalVideo || false),
|
||||
i18n,
|
||||
|
@ -60,8 +56,8 @@ story.add('Default', () => {
|
|||
return (
|
||||
<CallingLobby
|
||||
{...props}
|
||||
callDetails={{
|
||||
...callDetails,
|
||||
conversation={{
|
||||
...conversation,
|
||||
avatarPath: 'https://www.stevensegallery.com/600/600',
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
import React from 'react';
|
||||
import {
|
||||
CallDetailsType,
|
||||
SetLocalAudioType,
|
||||
SetLocalPreviewType,
|
||||
SetLocalVideoType,
|
||||
|
@ -15,10 +14,15 @@ import {
|
|||
} from './CallingButton';
|
||||
import { CallBackgroundBlur } from './CallBackgroundBlur';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import { ColorType } from '../types/Colors';
|
||||
|
||||
export type PropsType = {
|
||||
availableCameras: Array<MediaDeviceInfo>;
|
||||
callDetails: CallDetailsType;
|
||||
conversation: {
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
title: string;
|
||||
};
|
||||
hasLocalAudio: boolean;
|
||||
hasLocalVideo: boolean;
|
||||
i18n: LocalizerType;
|
||||
|
@ -34,7 +38,7 @@ export type PropsType = {
|
|||
|
||||
export const CallingLobby = ({
|
||||
availableCameras,
|
||||
callDetails,
|
||||
conversation,
|
||||
hasLocalAudio,
|
||||
hasLocalVideo,
|
||||
i18n,
|
||||
|
@ -50,20 +54,12 @@ export const CallingLobby = ({
|
|||
const localVideoRef = React.useRef(null);
|
||||
|
||||
const toggleAudio = React.useCallback((): void => {
|
||||
if (!callDetails) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLocalAudio({ enabled: !hasLocalAudio });
|
||||
}, [callDetails, hasLocalAudio, setLocalAudio]);
|
||||
}, [hasLocalAudio, setLocalAudio]);
|
||||
|
||||
const toggleVideo = React.useCallback((): void => {
|
||||
if (!callDetails) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLocalVideo({ enabled: !hasLocalVideo });
|
||||
}, [callDetails, hasLocalVideo, setLocalVideo]);
|
||||
}, [hasLocalVideo, setLocalVideo]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setLocalPreview({ element: localVideoRef });
|
||||
|
@ -112,7 +108,7 @@ export const CallingLobby = ({
|
|||
<div className="module-calling__container">
|
||||
<div className="module-calling__header">
|
||||
<div className="module-calling__header--header-name">
|
||||
{callDetails.title}
|
||||
{conversation.title}
|
||||
</div>
|
||||
<div className="module-calling-tools">
|
||||
{isGroupCall ? (
|
||||
|
@ -136,8 +132,8 @@ export const CallingLobby = ({
|
|||
<video ref={localVideoRef} autoPlay />
|
||||
) : (
|
||||
<CallBackgroundBlur
|
||||
avatarPath={callDetails.avatarPath}
|
||||
color={callDetails.color}
|
||||
avatarPath={conversation.avatarPath}
|
||||
color={conversation.color}
|
||||
>
|
||||
<div className="module-calling-lobby__video-off--icon" />
|
||||
<span className="module-calling-lobby__video-off--text">
|
||||
|
|
|
@ -13,11 +13,7 @@ import enMessages from '../../_locales/en/messages.json';
|
|||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const callDetails = {
|
||||
callId: 0,
|
||||
isIncoming: true,
|
||||
isVideoCall: true,
|
||||
|
||||
const conversation = {
|
||||
id: '3051234567',
|
||||
avatarPath: undefined,
|
||||
color: 'ultramarine' as ColorType,
|
||||
|
@ -28,7 +24,7 @@ const callDetails = {
|
|||
};
|
||||
|
||||
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||
callDetails: overrideProps.callDetails || callDetails,
|
||||
conversation: overrideProps.conversation || conversation,
|
||||
hangUp: action('hang-up'),
|
||||
hasLocalVideo: boolean('hasLocalVideo', overrideProps.hasLocalVideo || false),
|
||||
hasRemoteVideo: boolean(
|
||||
|
@ -50,8 +46,8 @@ story.add('Default', () => {
|
|||
|
||||
story.add('Contact (with avatar)', () => {
|
||||
const props = createProps({
|
||||
callDetails: {
|
||||
...callDetails,
|
||||
conversation: {
|
||||
...conversation,
|
||||
avatarPath: 'https://www.fillmurray.com/64/64',
|
||||
},
|
||||
});
|
||||
|
@ -60,8 +56,8 @@ story.add('Contact (with avatar)', () => {
|
|||
|
||||
story.add('Contact (no color)', () => {
|
||||
const props = createProps({
|
||||
callDetails: {
|
||||
...callDetails,
|
||||
conversation: {
|
||||
...conversation,
|
||||
color: undefined,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,28 +3,33 @@
|
|||
|
||||
import React from 'react';
|
||||
import {
|
||||
CallDetailsType,
|
||||
HangUpType,
|
||||
SetLocalPreviewType,
|
||||
SetRendererCanvasType,
|
||||
} from '../state/ducks/calling';
|
||||
import { Avatar } from './Avatar';
|
||||
import { CallBackgroundBlur } from './CallBackgroundBlur';
|
||||
import { ColorType } from '../types/Colors';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
|
||||
function renderAvatar(
|
||||
callDetails: CallDetailsType,
|
||||
i18n: LocalizerType
|
||||
): JSX.Element {
|
||||
const {
|
||||
{
|
||||
avatarPath,
|
||||
color,
|
||||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
} = callDetails;
|
||||
|
||||
}: {
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
title: string;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
},
|
||||
i18n: LocalizerType
|
||||
): JSX.Element {
|
||||
return (
|
||||
<div className="module-calling-pip__video--remote">
|
||||
<CallBackgroundBlur avatarPath={avatarPath} color={color}>
|
||||
|
@ -48,7 +53,15 @@ function renderAvatar(
|
|||
}
|
||||
|
||||
export type PropsType = {
|
||||
callDetails: CallDetailsType;
|
||||
conversation: {
|
||||
id: string;
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
title: string;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
};
|
||||
hangUp: (_: HangUpType) => void;
|
||||
hasLocalVideo: boolean;
|
||||
hasRemoteVideo: boolean;
|
||||
|
@ -64,7 +77,7 @@ const PIP_DEFAULT_Y = 56;
|
|||
const PIP_PADDING = 8;
|
||||
|
||||
export const CallingPip = ({
|
||||
callDetails,
|
||||
conversation,
|
||||
hangUp,
|
||||
hasLocalVideo,
|
||||
hasRemoteVideo,
|
||||
|
@ -204,7 +217,7 @@ export const CallingPip = ({
|
|||
ref={remoteVideoRef}
|
||||
/>
|
||||
) : (
|
||||
renderAvatar(callDetails, i18n)
|
||||
renderAvatar(conversation, i18n)
|
||||
)}
|
||||
{hasLocalVideo ? (
|
||||
<video
|
||||
|
@ -219,7 +232,7 @@ export const CallingPip = ({
|
|||
aria-label={i18n('calling__hangup')}
|
||||
className="module-calling-pip__button--hangup"
|
||||
onClick={() => {
|
||||
hangUp({ callId: callDetails.callId });
|
||||
hangUp({ conversationId: conversation.id });
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
|
|
|
@ -15,11 +15,13 @@ const i18n = setupI18n('en', enMessages);
|
|||
|
||||
const defaultProps = {
|
||||
acceptCall: action('accept-call'),
|
||||
callDetails: {
|
||||
call: {
|
||||
conversationId: 'fake-conversation-id',
|
||||
callId: 0,
|
||||
isIncoming: true,
|
||||
isVideoCall: true,
|
||||
|
||||
},
|
||||
conversation: {
|
||||
id: '3051234567',
|
||||
avatarPath: undefined,
|
||||
contactColor: 'ultramarine' as ColorType,
|
||||
|
@ -33,24 +35,15 @@ const defaultProps = {
|
|||
};
|
||||
|
||||
const permutations = [
|
||||
{
|
||||
title: 'Incoming Call Bar (no call details)',
|
||||
props: {},
|
||||
},
|
||||
{
|
||||
title: 'Incoming Call Bar (video)',
|
||||
props: {
|
||||
callDetails: {
|
||||
...defaultProps.callDetails,
|
||||
isVideoCall: true,
|
||||
},
|
||||
},
|
||||
props: {},
|
||||
},
|
||||
{
|
||||
title: 'Incoming Call Bar (audio)',
|
||||
props: {
|
||||
callDetails: {
|
||||
...defaultProps.callDetails,
|
||||
call: {
|
||||
...defaultProps.call,
|
||||
isVideoCall: false,
|
||||
},
|
||||
},
|
||||
|
@ -69,10 +62,13 @@ storiesOf('Components/IncomingCallBar', module)
|
|||
return (
|
||||
<IncomingCallBar
|
||||
{...defaultProps}
|
||||
callDetails={{
|
||||
...defaultProps.callDetails,
|
||||
color,
|
||||
call={{
|
||||
...defaultProps.call,
|
||||
isVideoCall,
|
||||
}}
|
||||
conversation={{
|
||||
...defaultProps.conversation,
|
||||
color,
|
||||
name,
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -6,17 +6,25 @@ import Tooltip from 'react-tooltip-lite';
|
|||
import { Avatar } from './Avatar';
|
||||
import { ContactName } from './conversation/ContactName';
|
||||
import { LocalizerType } from '../types/Util';
|
||||
import {
|
||||
AcceptCallType,
|
||||
CallDetailsType,
|
||||
DeclineCallType,
|
||||
} from '../state/ducks/calling';
|
||||
import { ColorType } from '../types/Colors';
|
||||
import { AcceptCallType, DeclineCallType } from '../state/ducks/calling';
|
||||
|
||||
export type PropsType = {
|
||||
acceptCall: (_: AcceptCallType) => void;
|
||||
callDetails?: CallDetailsType;
|
||||
declineCall: (_: DeclineCallType) => void;
|
||||
i18n: LocalizerType;
|
||||
call: {
|
||||
isVideoCall: boolean;
|
||||
};
|
||||
conversation: {
|
||||
id: string;
|
||||
avatarPath?: string;
|
||||
color?: ColorType;
|
||||
title: string;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
};
|
||||
};
|
||||
|
||||
type CallButtonProps = {
|
||||
|
@ -54,23 +62,21 @@ const CallButton = ({
|
|||
|
||||
export const IncomingCallBar = ({
|
||||
acceptCall,
|
||||
callDetails,
|
||||
declineCall,
|
||||
i18n,
|
||||
call,
|
||||
conversation,
|
||||
}: PropsType): JSX.Element | null => {
|
||||
if (!callDetails) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { isVideoCall } = call;
|
||||
const {
|
||||
id: conversationId,
|
||||
avatarPath,
|
||||
callId,
|
||||
color,
|
||||
title,
|
||||
name,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
} = callDetails;
|
||||
} = conversation;
|
||||
|
||||
return (
|
||||
<div className="module-incoming-call">
|
||||
|
@ -103,21 +109,17 @@ export const IncomingCallBar = ({
|
|||
dir="auto"
|
||||
className="module-incoming-call__contact--message-text"
|
||||
>
|
||||
{i18n(
|
||||
callDetails.isVideoCall
|
||||
? 'incomingVideoCall'
|
||||
: 'incomingAudioCall'
|
||||
)}
|
||||
{i18n(isVideoCall ? 'incomingVideoCall' : 'incomingAudioCall')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="module-incoming-call__actions">
|
||||
{callDetails.isVideoCall ? (
|
||||
{isVideoCall ? (
|
||||
<>
|
||||
<CallButton
|
||||
classSuffix="decline"
|
||||
onClick={() => {
|
||||
declineCall({ callId });
|
||||
declineCall({ conversationId });
|
||||
}}
|
||||
tabIndex={0}
|
||||
tooltipContent={i18n('declineCall')}
|
||||
|
@ -125,7 +127,7 @@ export const IncomingCallBar = ({
|
|||
<CallButton
|
||||
classSuffix="accept-video-as-audio"
|
||||
onClick={() => {
|
||||
acceptCall({ callId, asVideoCall: false });
|
||||
acceptCall({ conversationId, asVideoCall: false });
|
||||
}}
|
||||
tabIndex={0}
|
||||
tooltipContent={i18n('acceptCallWithoutVideo')}
|
||||
|
@ -133,7 +135,7 @@ export const IncomingCallBar = ({
|
|||
<CallButton
|
||||
classSuffix="accept-video"
|
||||
onClick={() => {
|
||||
acceptCall({ callId, asVideoCall: true });
|
||||
acceptCall({ conversationId, asVideoCall: true });
|
||||
}}
|
||||
tabIndex={0}
|
||||
tooltipContent={i18n('acceptCall')}
|
||||
|
@ -144,7 +146,7 @@ export const IncomingCallBar = ({
|
|||
<CallButton
|
||||
classSuffix="decline"
|
||||
onClick={() => {
|
||||
declineCall({ callId });
|
||||
declineCall({ conversationId });
|
||||
}}
|
||||
tabIndex={0}
|
||||
tooltipContent={i18n('declineCall')}
|
||||
|
@ -152,7 +154,7 @@ export const IncomingCallBar = ({
|
|||
<CallButton
|
||||
classSuffix="accept-audio"
|
||||
onClick={() => {
|
||||
acceptCall({ callId, asVideoCall: false });
|
||||
acceptCall({ conversationId, asVideoCall: false });
|
||||
}}
|
||||
tabIndex={0}
|
||||
tooltipContent={i18n('acceptCall')}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue