Top level Reaction/Raise Hand buttons and remove More Options
This commit is contained in:
parent
a6e744dcbc
commit
670da5722a
8 changed files with 141 additions and 151 deletions
|
@ -1748,6 +1748,10 @@
|
|||
"messageformat": "Stop presenting",
|
||||
"description": "Button tooltip label for stopping screen sharing"
|
||||
},
|
||||
"icu:calling__button--react": {
|
||||
"messageformat": "React",
|
||||
"description": "Button tooltip label to send a reaction during a call"
|
||||
},
|
||||
"icu:calling__button--ring__disabled-because-group-is-too-large": {
|
||||
"messageformat": "Group is too large to ring the participants.",
|
||||
"description": "Button tooltip label when you can't ring because the group is too large"
|
||||
|
|
|
@ -4114,6 +4114,7 @@ button.module-image__border-overlay:focus {
|
|||
&__footer {
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
z-index: $z-index-above-base;
|
||||
|
|
|
@ -293,5 +293,5 @@ $NavTabs__Item__blockPadding: 2px;
|
|||
$NavTabs__Toggle__blockPadding: 8px;
|
||||
$NavTabs__ItemButton__blockPadding: 10px;
|
||||
$CallControls__height: 80px;
|
||||
$CallControls__max-width: 600px;
|
||||
$CallControls__initial-width: 440px;
|
||||
$CallControls__max-width: 640px;
|
||||
$CallControls__initial-width: 480px;
|
||||
|
|
|
@ -110,7 +110,7 @@
|
|||
margin-block: -5px;
|
||||
}
|
||||
|
||||
.CallControls__MoreOptionsButtonContainer--menu-shown .module-tooltip {
|
||||
.CallControls__ReactButtonContainer--menu-shown .module-tooltip {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
@ -120,14 +120,12 @@
|
|||
flex-basis: calc($local-preview-width + 16px);
|
||||
}
|
||||
|
||||
.CallControls__MoreOptionsContainer {
|
||||
.CallControls__ReactionPickerContainer {
|
||||
position: absolute;
|
||||
inset-inline-start: min(48%, 40vw);
|
||||
inset-inline-start: min(44%, 32vw);
|
||||
inset-block-end: 70px;
|
||||
z-index: $z-index-toast;
|
||||
}
|
||||
|
||||
.CallControls__MoreOptionsMenu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: calc(100vh - 155px);
|
||||
|
@ -136,48 +134,13 @@
|
|||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.CallControls__MoreOptionsMenu .module-emoji-picker {
|
||||
.CallControls__ReactionPickerContainer .module-emoji-picker {
|
||||
margin-bottom: auto;
|
||||
max-width: calc(100vw / 2 + 20px);
|
||||
}
|
||||
|
||||
.CallControls__MoreOptionsMenu
|
||||
.module-emoji-picker
|
||||
+ .CallControls__MenuItemRaiseHand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.CallControls__MoreOptionsMenu .module-ReactionPickerPicker {
|
||||
.CallControls__ReactionPickerContainer .module-ReactionPickerPicker {
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
animation-duration: 200ms;
|
||||
}
|
||||
}
|
||||
|
||||
.CallControls__MenuItemRaiseHand {
|
||||
@include button-reset;
|
||||
display: flex;
|
||||
min-width: 290px;
|
||||
padding-block: 12px;
|
||||
padding-inline: 12px;
|
||||
margin-block-start: 8px;
|
||||
border-radius: 10px;
|
||||
align-items: center;
|
||||
text-align: start;
|
||||
background-color: $color-gray-75;
|
||||
color: $color-white;
|
||||
filter: drop-shadow(0px 4px 3px $color-black-alpha-20);
|
||||
}
|
||||
|
||||
.CallControls__MenuItemRaiseHand:hover {
|
||||
background-color: $color-gray-65;
|
||||
}
|
||||
|
||||
.CallControls__MenuItemRaiseHandIcon {
|
||||
@include color-svg(
|
||||
'../images/icons/v3/raise_hand/raise_hand-light.svg',
|
||||
$color-gray-15
|
||||
);
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-inline: 2px 12px;
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
|
||||
div {
|
||||
@include color-svg($icon, $icon-color);
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,26 @@
|
|||
);
|
||||
}
|
||||
|
||||
&--raise-hand {
|
||||
$icon: '../images/icons/v3/raise_hand/raise_hand-light.svg';
|
||||
&--on {
|
||||
@include calling-button-icon-highlighted($icon);
|
||||
}
|
||||
&--off {
|
||||
@include calling-button-icon-regular($icon);
|
||||
}
|
||||
}
|
||||
|
||||
&--react {
|
||||
$icon: '../images/icons/v3/heart/heart-plus.svg';
|
||||
&--on {
|
||||
@include calling-button-icon-highlighted($icon);
|
||||
}
|
||||
&--off {
|
||||
@include calling-button-icon-regular($icon);
|
||||
}
|
||||
}
|
||||
|
||||
&--ring {
|
||||
$icon: '../images/icons/v3/bell/bell-slash-fill.svg';
|
||||
&--on {
|
||||
|
|
|
@ -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,18 +823,17 @@ export function CallScreen({
|
|||
<div className="CallControls__Status">{callStatus}</div>
|
||||
</div>
|
||||
|
||||
{showMoreOptions && (
|
||||
<div className="CallControls__MoreOptionsContainer">
|
||||
{showReactionPicker && (
|
||||
<div
|
||||
className="CallControls__MoreOptionsMenu"
|
||||
ref={moreOptionsMenuRef}
|
||||
className="CallControls__ReactionPickerContainer"
|
||||
ref={reactionPickerContainerRef}
|
||||
>
|
||||
{isGroupCallReactionsEnabled &&
|
||||
renderReactionPicker({
|
||||
ref: reactionPickerRef,
|
||||
onClose: () => setShowMoreOptions(false),
|
||||
onClose: () => setShowReactionPicker(false),
|
||||
onPick: emoji => {
|
||||
setShowMoreOptions(false);
|
||||
setShowReactionPicker(false);
|
||||
sendGroupCallReaction({
|
||||
conversationId: conversation.id,
|
||||
value: emoji,
|
||||
|
@ -826,34 +841,10 @@ export function CallScreen({
|
|||
},
|
||||
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>
|
||||
</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 && (
|
||||
<div
|
||||
className={classNames(
|
||||
'CallControls__MoreOptionsButtonContainer',
|
||||
{
|
||||
'CallControls__MoreOptionsButtonContainer--menu-shown':
|
||||
showMoreOptions,
|
||||
}
|
||||
)}
|
||||
ref={moreOptionsButtonRef}
|
||||
>
|
||||
<CallingButton
|
||||
buttonType={CallingButtonType.MORE_OPTIONS}
|
||||
buttonType={presentingButtonType}
|
||||
i18n={i18n}
|
||||
onMouseEnter={onControlsMouseEnter}
|
||||
onMouseLeave={onControlsMouseLeave}
|
||||
onClick={toggleMoreOptions}
|
||||
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__ReactButtonContainer', {
|
||||
'CallControls__ReactButtonContainer--menu-shown':
|
||||
showReactionPicker,
|
||||
})}
|
||||
ref={reactButtonRef}
|
||||
>
|
||||
<CallingButton
|
||||
buttonType={reactButtonType}
|
||||
i18n={i18n}
|
||||
onMouseEnter={onControlsMouseEnter}
|
||||
onMouseLeave={onControlsMouseLeave}
|
||||
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>
|
||||
);
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue