CallScreen interactivity fixes
This commit is contained in:
parent
427055ea47
commit
4dcbb7352f
4 changed files with 64 additions and 37 deletions
|
@ -75,6 +75,41 @@ export type PropsType = {
|
||||||
toggleSpeakerView: () => void;
|
toggleSpeakerView: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DirectCallHeaderMessagePropsType = {
|
||||||
|
i18n: LocalizerType;
|
||||||
|
callState: CallState;
|
||||||
|
joinedAt?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function DirectCallHeaderMessage({
|
||||||
|
callState,
|
||||||
|
i18n,
|
||||||
|
joinedAt,
|
||||||
|
}: DirectCallHeaderMessagePropsType): JSX.Element | null {
|
||||||
|
const [acceptedDuration, setAcceptedDuration] = useState<
|
||||||
|
number | undefined
|
||||||
|
>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!joinedAt) {
|
||||||
|
return noop;
|
||||||
|
}
|
||||||
|
// It's really jumpy with a value of 500ms.
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setAcceptedDuration(Date.now() - joinedAt);
|
||||||
|
}, 100);
|
||||||
|
return clearInterval.bind(null, interval);
|
||||||
|
}, [joinedAt]);
|
||||||
|
|
||||||
|
if (callState === CallState.Reconnecting) {
|
||||||
|
return <>{i18n('callReconnecting')}</>;
|
||||||
|
}
|
||||||
|
if (callState === CallState.Accepted && acceptedDuration) {
|
||||||
|
return <>{i18n('callDuration', [renderDuration(acceptedDuration)])}</>;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
export const CallScreen: React.FC<PropsType> = ({
|
export const CallScreen: React.FC<PropsType> = ({
|
||||||
activeCall,
|
activeCall,
|
||||||
getGroupCallVideoFrameSource,
|
getGroupCallVideoFrameSource,
|
||||||
|
@ -145,7 +180,6 @@ export const CallScreen: React.FC<PropsType> = ({
|
||||||
setControlsHover(false);
|
setControlsHover(false);
|
||||||
}, [setControlsHover]);
|
}, [setControlsHover]);
|
||||||
|
|
||||||
const [acceptedDuration, setAcceptedDuration] = useState<number | null>(null);
|
|
||||||
const [showControls, setShowControls] = useState(true);
|
const [showControls, setShowControls] = useState(true);
|
||||||
|
|
||||||
const localVideoRef = useRef<HTMLVideoElement | null>(null);
|
const localVideoRef = useRef<HTMLVideoElement | null>(null);
|
||||||
|
@ -157,17 +191,6 @@ export const CallScreen: React.FC<PropsType> = ({
|
||||||
};
|
};
|
||||||
}, [setLocalPreview, setRendererCanvas]);
|
}, [setLocalPreview, setRendererCanvas]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!joinedAt) {
|
|
||||||
return noop;
|
|
||||||
}
|
|
||||||
// It's really jumpy with a value of 500ms.
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
setAcceptedDuration(Date.now() - joinedAt);
|
|
||||||
}, 100);
|
|
||||||
return clearInterval.bind(null, interval);
|
|
||||||
}, [joinedAt]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showControls || stickyControls || controlsHover) {
|
if (!showControls || stickyControls || controlsHover) {
|
||||||
return noop;
|
return noop;
|
||||||
|
@ -175,7 +198,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
setShowControls(false);
|
setShowControls(false);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
return clearInterval.bind(null, timer);
|
return clearTimeout.bind(null, timer);
|
||||||
}, [showControls, stickyControls, controlsHover]);
|
}, [showControls, stickyControls, controlsHover]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -215,7 +238,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
||||||
|
|
||||||
let isRinging: boolean;
|
let isRinging: boolean;
|
||||||
let hasCallStarted: boolean;
|
let hasCallStarted: boolean;
|
||||||
let headerMessage: string | undefined;
|
let headerMessage: ReactNode | undefined;
|
||||||
let headerTitle: string | undefined;
|
let headerTitle: string | undefined;
|
||||||
let isConnected: boolean;
|
let isConnected: boolean;
|
||||||
let participantCount: number;
|
let participantCount: number;
|
||||||
|
@ -227,10 +250,12 @@ export const CallScreen: React.FC<PropsType> = ({
|
||||||
activeCall.callState === CallState.Prering ||
|
activeCall.callState === CallState.Prering ||
|
||||||
activeCall.callState === CallState.Ringing;
|
activeCall.callState === CallState.Ringing;
|
||||||
hasCallStarted = !isRinging;
|
hasCallStarted = !isRinging;
|
||||||
headerMessage = renderDirectCallHeaderMessage(
|
headerMessage = (
|
||||||
i18n,
|
<DirectCallHeaderMessage
|
||||||
activeCall.callState || CallState.Prering,
|
i18n={i18n}
|
||||||
acceptedDuration
|
callState={activeCall.callState || CallState.Prering}
|
||||||
|
joinedAt={joinedAt}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
headerTitle = isRinging ? undefined : conversation.title;
|
headerTitle = isRinging ? undefined : conversation.title;
|
||||||
isConnected = activeCall.callState === CallState.Accepted;
|
isConnected = activeCall.callState === CallState.Accepted;
|
||||||
|
@ -252,7 +277,6 @@ export const CallScreen: React.FC<PropsType> = ({
|
||||||
activeCall.outgoingRing && !activeCall.remoteParticipants.length;
|
activeCall.outgoingRing && !activeCall.remoteParticipants.length;
|
||||||
hasCallStarted = activeCall.joinState !== GroupCallJoinState.NotJoined;
|
hasCallStarted = activeCall.joinState !== GroupCallJoinState.NotJoined;
|
||||||
participantCount = activeCall.remoteParticipants.length + 1;
|
participantCount = activeCall.remoteParticipants.length + 1;
|
||||||
headerMessage = undefined;
|
|
||||||
|
|
||||||
if (isRinging) {
|
if (isRinging) {
|
||||||
headerTitle = undefined;
|
headerTitle = undefined;
|
||||||
|
@ -399,6 +423,9 @@ export const CallScreen: React.FC<PropsType> = ({
|
||||||
hasCallStarted ? 'call-started' : 'call-not-started'
|
hasCallStarted ? 'call-started' : 'call-not-started'
|
||||||
}`
|
}`
|
||||||
)}
|
)}
|
||||||
|
onFocus={() => {
|
||||||
|
setShowControls(true);
|
||||||
|
}}
|
||||||
onMouseMove={() => {
|
onMouseMove={() => {
|
||||||
setShowControls(true);
|
setShowControls(true);
|
||||||
}}
|
}}
|
||||||
|
@ -451,27 +478,33 @@ export const CallScreen: React.FC<PropsType> = ({
|
||||||
'module-ongoing-call__footer__actions',
|
'module-ongoing-call__footer__actions',
|
||||||
controlsFadeClass
|
controlsFadeClass
|
||||||
)}
|
)}
|
||||||
onMouseEnter={onControlsMouseEnter}
|
|
||||||
onMouseLeave={onControlsMouseLeave}
|
|
||||||
>
|
>
|
||||||
<CallingButton
|
<CallingButton
|
||||||
buttonType={presentingButtonType}
|
buttonType={presentingButtonType}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
onMouseEnter={onControlsMouseEnter}
|
||||||
|
onMouseLeave={onControlsMouseLeave}
|
||||||
onClick={togglePresenting}
|
onClick={togglePresenting}
|
||||||
/>
|
/>
|
||||||
<CallingButton
|
<CallingButton
|
||||||
buttonType={videoButtonType}
|
buttonType={videoButtonType}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
onMouseEnter={onControlsMouseEnter}
|
||||||
|
onMouseLeave={onControlsMouseLeave}
|
||||||
onClick={toggleVideo}
|
onClick={toggleVideo}
|
||||||
/>
|
/>
|
||||||
<CallingButton
|
<CallingButton
|
||||||
buttonType={audioButtonType}
|
buttonType={audioButtonType}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
onMouseEnter={onControlsMouseEnter}
|
||||||
|
onMouseLeave={onControlsMouseLeave}
|
||||||
onClick={toggleAudio}
|
onClick={toggleAudio}
|
||||||
/>
|
/>
|
||||||
<CallingButton
|
<CallingButton
|
||||||
buttonType={CallingButtonType.HANG_UP}
|
buttonType={CallingButtonType.HANG_UP}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
onMouseEnter={onControlsMouseEnter}
|
||||||
|
onMouseLeave={onControlsMouseLeave}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
hangUp({ conversationId: conversation.id });
|
hangUp({ conversationId: conversation.id });
|
||||||
}}
|
}}
|
||||||
|
@ -502,20 +535,6 @@ function getCallModeClassSuffix(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDirectCallHeaderMessage(
|
|
||||||
i18n: LocalizerType,
|
|
||||||
callState: CallState,
|
|
||||||
acceptedDuration: null | number
|
|
||||||
): string | undefined {
|
|
||||||
if (callState === CallState.Reconnecting) {
|
|
||||||
return i18n('callReconnecting');
|
|
||||||
}
|
|
||||||
if (callState === CallState.Accepted && acceptedDuration) {
|
|
||||||
return i18n('callDuration', [renderDuration(acceptedDuration)]);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderDuration(ms: number): string {
|
function renderDuration(ms: number): string {
|
||||||
const secs = Math.floor((ms / 1000) % 60)
|
const secs = Math.floor((ms / 1000) % 60)
|
||||||
.toString()
|
.toString()
|
||||||
|
|
|
@ -19,6 +19,8 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
select('buttonType', CallingButtonType, CallingButtonType.HANG_UP),
|
select('buttonType', CallingButtonType, CallingButtonType.HANG_UP),
|
||||||
i18n,
|
i18n,
|
||||||
onClick: action('on-click'),
|
onClick: action('on-click'),
|
||||||
|
onMouseEnter: action('on-mouse-enter'),
|
||||||
|
onMouseLeave: action('on-mouse-leave'),
|
||||||
tooltipDirection: select(
|
tooltipDirection: select(
|
||||||
'tooltipDirection',
|
'tooltipDirection',
|
||||||
TooltipPlacement,
|
TooltipPlacement,
|
||||||
|
|
|
@ -29,6 +29,8 @@ export type PropsType = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
isVisible?: boolean;
|
isVisible?: boolean;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
|
onMouseEnter?: () => void;
|
||||||
|
onMouseLeave?: () => void;
|
||||||
tooltipDirection?: TooltipPlacement;
|
tooltipDirection?: TooltipPlacement;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,6 +39,8 @@ export const CallingButton = ({
|
||||||
i18n,
|
i18n,
|
||||||
isVisible = true,
|
isVisible = true,
|
||||||
onClick,
|
onClick,
|
||||||
|
onMouseEnter,
|
||||||
|
onMouseLeave,
|
||||||
tooltipDirection,
|
tooltipDirection,
|
||||||
}: PropsType): JSX.Element => {
|
}: PropsType): JSX.Element => {
|
||||||
const uniqueButtonId = useMemo(() => uuid(), []);
|
const uniqueButtonId = useMemo(() => uuid(), []);
|
||||||
|
@ -128,6 +132,8 @@ export const CallingButton = ({
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
id={uniqueButtonId}
|
id={uniqueButtonId}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div />
|
<div />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2020-2021 Signal Messenger, LLC
|
// Copyright 2020-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { LocalizerType } from '../types/Util';
|
import { LocalizerType } from '../types/Util';
|
||||||
import { Tooltip } from './Tooltip';
|
import { Tooltip } from './Tooltip';
|
||||||
|
@ -11,7 +11,7 @@ export type PropsType = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
isInSpeakerView?: boolean;
|
isInSpeakerView?: boolean;
|
||||||
isGroupCall?: boolean;
|
isGroupCall?: boolean;
|
||||||
message?: string;
|
message?: ReactNode;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
participantCount: number;
|
participantCount: number;
|
||||||
showParticipantsList: boolean;
|
showParticipantsList: boolean;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue