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

@ -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"

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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 {

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",