Top level Reaction/Raise Hand buttons and remove More Options

This commit is contained in:
ayumi-signal 2024-01-17 12:29:44 -08:00 committed by GitHub
parent a6e744dcbc
commit 670da5722a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 141 additions and 151 deletions

View file

@ -72,7 +72,6 @@ import {
useCallingToasts,
} from './CallingToast';
import { Spinner } from './Spinner';
import { handleOutsideClick } from '../util/handleOutsideClick';
import type { Props as ReactionPickerProps } from './conversation/ReactionPicker';
import type { SmartReactionPicker } from '../state/smart/ReactionPicker';
import { Emoji } from './emoji/Emoji';
@ -250,12 +249,12 @@ export function CallScreen({
hangUpActiveCall('button click');
}, [hangUpActiveCall]);
const moreOptionsMenuRef = React.useRef<null | HTMLDivElement>(null);
const moreOptionsButtonRef = React.useRef<null | HTMLDivElement>(null);
const reactButtonRef = React.useRef<null | HTMLDivElement>(null);
const reactionPickerRef = React.useRef<null | HTMLDivElement>(null);
const [showMoreOptions, setShowMoreOptions] = useState(false);
const toggleMoreOptions = useCallback(() => {
setShowMoreOptions(prevValue => !prevValue);
const reactionPickerContainerRef = React.useRef<null | HTMLDivElement>(null);
const [showReactionPicker, setShowReactionPicker] = useState(false);
const toggleReactionPicker = useCallback(() => {
setShowReactionPicker(prevValue => !prevValue);
}, []);
const [showRaisedHandsList, setShowRaisedHandsList] = useState(false);
@ -285,14 +284,19 @@ export function CallScreen({
}, [setLocalPreview, setRendererCanvas]);
useEffect(() => {
if (!showControls || showMoreOptions || stickyControls || controlsHover) {
if (
!showControls ||
showReactionPicker ||
stickyControls ||
controlsHover
) {
return noop;
}
const timer = setTimeout(() => {
setShowControls(false);
}, 5000);
return clearTimeout.bind(null, timer);
}, [showControls, showMoreOptions, stickyControls, controlsHover]);
}, [showControls, showReactionPicker, stickyControls, controlsHover]);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent): void => {
@ -321,22 +325,6 @@ export function CallScreen({
};
}, [toggleAudio, toggleVideo]);
useEffect(() => {
if (!showMoreOptions) {
return noop;
}
return handleOutsideClick(
() => {
setShowMoreOptions(false);
return true;
},
{
containerElements: [moreOptionsButtonRef, moreOptionsMenuRef],
name: 'CallScreen.moreOptions',
}
);
}, [showMoreOptions]);
useScreenSharingStoppedToast({ activeCall, i18n });
useViewModeChangedToast({ activeCall, i18n });
@ -468,8 +456,6 @@ export function CallScreen({
});
const isGroupCall = activeCall.callMode === CallMode.Group;
const isMoreOptionsButtonEnabled =
isGroupCall && (isGroupCallRaiseHandEnabled || isGroupCallReactionsEnabled);
let presentingButtonType: CallingButtonType;
if (presentingSource) {
@ -518,6 +504,17 @@ export function CallScreen({
]
);
let raiseHandButtonType: CallingButtonType | undefined;
let reactButtonType: CallingButtonType | undefined;
if (isGroupCall) {
raiseHandButtonType = localHandRaised
? CallingButtonType.RAISE_HAND_ON
: CallingButtonType.RAISE_HAND_OFF;
reactButtonType = showReactionPicker
? CallingButtonType.REACT_ON
: CallingButtonType.REACT_OFF;
}
const renderRaisedHandsToast = React.useCallback(
(hands: Array<number>) => {
// Sort "You" to the front.
@ -793,8 +790,27 @@ export function CallScreen({
renderRaisedHandsToast={renderRaisedHandsToast}
i18n={i18n}
/>
{/* We render the local preview first and set the footer flex direction to row-reverse
to ensure the preview is visible at low viewport widths. */}
<div className="module-ongoing-call__footer">
<div className="module-calling__spacer CallControls__OuterSpacer" />
{localPreviewNode ? (
<div className="module-ongoing-call__footer__local-preview module-ongoing-call__footer__local-preview--active">
{localPreviewNode}
{!isSendingVideo && (
<div className="CallingStatusIndicator CallingStatusIndicator--Video" />
)}
<CallingAudioIndicator
hasAudio={hasLocalAudio}
audioLevel={localAudioLevel}
shouldShowSpeaking={isSpeaking}
/>
{syncedLocalHandRaised && (
<div className="CallingStatusIndicator CallingStatusIndicator--HandRaised" />
)}
</div>
) : (
<div className="module-ongoing-call__footer__local-preview" />
)}
<div
className={classNames(
'CallControls',
@ -807,53 +823,28 @@ export function CallScreen({
<div className="CallControls__Status">{callStatus}</div>
</div>
{showMoreOptions && (
<div className="CallControls__MoreOptionsContainer">
<div
className="CallControls__MoreOptionsMenu"
ref={moreOptionsMenuRef}
>
{isGroupCallReactionsEnabled &&
renderReactionPicker({
ref: reactionPickerRef,
onClose: () => setShowMoreOptions(false),
onPick: emoji => {
setShowMoreOptions(false);
sendGroupCallReaction({
conversationId: conversation.id,
value: emoji,
});
},
renderEmojiPicker,
})}
{isGroupCallRaiseHandEnabled && (
<button
className="CallControls__MenuItemRaiseHand"
onClick={() => {
setShowMoreOptions(false);
toggleRaiseHand();
}}
type="button"
>
<span className="CallControls__MenuItemRaiseHandIcon" />
{localHandRaised
? i18n('icu:CallControls__MenuItemRaiseHand--lower')
: i18n('icu:CallControls__MenuItemRaiseHand')}
</button>
)}
</div>
{showReactionPicker && (
<div
className="CallControls__ReactionPickerContainer"
ref={reactionPickerContainerRef}
>
{isGroupCallReactionsEnabled &&
renderReactionPicker({
ref: reactionPickerRef,
onClose: () => setShowReactionPicker(false),
onPick: emoji => {
setShowReactionPicker(false);
sendGroupCallReaction({
conversationId: conversation.id,
value: emoji,
});
},
renderEmojiPicker,
})}
</div>
)}
<div className="CallControls__ButtonContainer">
<CallingButton
buttonType={presentingButtonType}
i18n={i18n}
onMouseEnter={onControlsMouseEnter}
onMouseLeave={onControlsMouseLeave}
onClick={togglePresenting}
tooltipDirection={TooltipPlacement.Top}
/>
<CallingButton
buttonType={videoButtonType}
i18n={i18n}
@ -870,23 +861,38 @@ export function CallScreen({
onClick={toggleAudio}
tooltipDirection={TooltipPlacement.Top}
/>
{isMoreOptionsButtonEnabled && (
<CallingButton
buttonType={presentingButtonType}
i18n={i18n}
onMouseEnter={onControlsMouseEnter}
onMouseLeave={onControlsMouseLeave}
onClick={togglePresenting}
tooltipDirection={TooltipPlacement.Top}
/>
{isGroupCallRaiseHandEnabled && raiseHandButtonType && (
<CallingButton
buttonType={raiseHandButtonType}
i18n={i18n}
onMouseEnter={onControlsMouseEnter}
onMouseLeave={onControlsMouseLeave}
onClick={() => toggleRaiseHand()}
tooltipDirection={TooltipPlacement.Top}
/>
)}
{isGroupCallReactionsEnabled && reactButtonType && (
<div
className={classNames(
'CallControls__MoreOptionsButtonContainer',
{
'CallControls__MoreOptionsButtonContainer--menu-shown':
showMoreOptions,
}
)}
ref={moreOptionsButtonRef}
className={classNames('CallControls__ReactButtonContainer', {
'CallControls__ReactButtonContainer--menu-shown':
showReactionPicker,
})}
ref={reactButtonRef}
>
<CallingButton
buttonType={CallingButtonType.MORE_OPTIONS}
buttonType={reactButtonType}
i18n={i18n}
onMouseEnter={onControlsMouseEnter}
onMouseLeave={onControlsMouseLeave}
onClick={toggleMoreOptions}
onClick={toggleReactionPicker}
tooltipDirection={TooltipPlacement.Top}
/>
</div>
@ -908,24 +914,7 @@ export function CallScreen({
</Button>
</div>
</div>
{localPreviewNode ? (
<div className="module-ongoing-call__footer__local-preview module-ongoing-call__footer__local-preview--active">
{localPreviewNode}
{!isSendingVideo && (
<div className="CallingStatusIndicator CallingStatusIndicator--Video" />
)}
<CallingAudioIndicator
hasAudio={hasLocalAudio}
audioLevel={localAudioLevel}
shouldShowSpeaking={isSpeaking}
/>
{syncedLocalHandRaised && (
<div className="CallingStatusIndicator CallingStatusIndicator--HandRaised" />
)}
</div>
) : (
<div className="module-ongoing-call__footer__local-preview" />
)}
<div className="module-calling__spacer CallControls__OuterSpacer" />
</div>
</div>
);

View file

@ -16,6 +16,10 @@ export enum CallingButtonType {
PRESENTING_DISABLED = 'PRESENTING_DISABLED',
PRESENTING_OFF = 'PRESENTING_OFF',
PRESENTING_ON = 'PRESENTING_ON',
RAISE_HAND_OFF = 'RAISE_HAND_OFF',
RAISE_HAND_ON = 'RAISE_HAND_ON',
REACT_OFF = 'REACT_OFF',
REACT_ON = 'REACT_ON',
RING_DISABLED = 'RING_DISABLED',
RING_OFF = 'RING_OFF',
RING_ON = 'RING_ON',
@ -75,6 +79,17 @@ export function CallingButton({
tooltipContent = i18n(
'icu:calling__button--ring__disabled-because-group-is-too-large'
);
} else if (buttonType === CallingButtonType.REACT_OFF) {
classNameSuffix = 'react--off';
tooltipContent = i18n('icu:calling__button--react');
} else if (buttonType === CallingButtonType.REACT_ON) {
classNameSuffix = 'react--on';
} else if (buttonType === CallingButtonType.RAISE_HAND_OFF) {
classNameSuffix = 'raise-hand--off';
tooltipContent = i18n('icu:CallControls__MenuItemRaiseHand');
} else if (buttonType === CallingButtonType.RAISE_HAND_ON) {
classNameSuffix = 'raise-hand--on';
tooltipContent = i18n('icu:CallControls__MenuItemRaiseHand--lower');
} else if (buttonType === CallingButtonType.RING_OFF) {
classNameSuffix = 'ring--off';
tooltipContent = i18n('icu:CallingButton--ring-on');

View file

@ -2827,18 +2827,16 @@
{
"rule": "React-useRef",
"path": "ts/components/CallScreen.tsx",
"line": " const moreOptionsMenuRef = React.useRef<null | HTMLDivElement>(null);",
"line": " const reactButtonRef = React.useRef<null | HTMLDivElement>(null);",
"reasonCategory": "usageTrusted",
"updated": "2023-11-14T23:29:51.425Z",
"reasonDetail": "Used to detect clicks outside of the Calling More Options button menu"
"updated": "2024-01-16T22:59:06.336Z"
},
{
"rule": "React-useRef",
"path": "ts/components/CallScreen.tsx",
"line": " const moreOptionsButtonRef = React.useRef<null | HTMLDivElement>(null);",
"line": " const reactionPickerContainerRef = React.useRef<null | HTMLDivElement>(null);",
"reasonCategory": "usageTrusted",
"updated": "2023-11-14T23:29:51.425Z",
"reasonDetail": "Used to detect clicks outside of the Calling More Options button menu and ensures clicking the button does not re-open the menu."
"updated": "2024-01-16T22:59:06.336Z"
},
{
"rule": "React-useRef",