Animate call link join requests
This commit is contained in:
parent
7e9773a144
commit
e51cde1770
5 changed files with 117 additions and 15 deletions
|
@ -14,10 +14,13 @@
|
|||
width: 364px;
|
||||
padding-inline: 0;
|
||||
padding-block-start: 0;
|
||||
background: $color-gray-78;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.CallingPendingParticipants--Expandable {
|
||||
background: $color-gray-78;
|
||||
}
|
||||
|
||||
.CallingPendingParticipants--Expanded {
|
||||
padding-block-end: 2px;
|
||||
}
|
||||
|
|
|
@ -858,7 +858,7 @@ export function CallScreen({
|
|||
renderRaisedHandsToast={renderRaisedHandsToast}
|
||||
i18n={i18n}
|
||||
/>
|
||||
{pendingParticipants.length ? (
|
||||
{isCallLinkAdmin ? (
|
||||
<CallingPendingParticipants
|
||||
i18n={i18n}
|
||||
participants={pendingParticipants}
|
||||
|
|
|
@ -60,6 +60,26 @@ export function Many(): JSX.Element {
|
|||
);
|
||||
}
|
||||
|
||||
export function Changing(): JSX.Element {
|
||||
const counts = [0, 1, 2, 3, 2, 1];
|
||||
const [countIndex, setCountIndex] = React.useState<number>(0);
|
||||
React.useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setCountIndex((countIndex + 1) % counts.length);
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [countIndex, counts.length]);
|
||||
|
||||
return (
|
||||
<CallingPendingParticipants
|
||||
{...createProps({
|
||||
participants: allRemoteParticipants.slice(0, counts[countIndex]),
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function ExpandedOne(): JSX.Element {
|
||||
return (
|
||||
<CallingPendingParticipants
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { noop } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { animated, useSpring } from '@react-spring/web';
|
||||
import { Avatar, AvatarSize } from './Avatar';
|
||||
import { ContactName } from './conversation/ContactName';
|
||||
import { InContactsIcon } from './InContactsIcon';
|
||||
|
@ -20,6 +22,9 @@ import type { ServiceIdString } from '../types/ServiceId';
|
|||
import { handleOutsideClick } from '../util/handleOutsideClick';
|
||||
import { Theme } from '../util/theme';
|
||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||
import { usePrevious } from '../hooks/usePrevious';
|
||||
import { useReducedMotion } from '../hooks/useReducedMotion';
|
||||
import { drop } from '../util/drop';
|
||||
|
||||
enum ConfirmDialogState {
|
||||
None = 'None',
|
||||
|
@ -49,11 +54,33 @@ export function CallingPendingParticipants({
|
|||
denyUser,
|
||||
toggleCallLinkPendingParticipantModal,
|
||||
}: PropsType): JSX.Element | null {
|
||||
const reducedMotion = useReducedMotion();
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const [opacitySpringProps, opacitySpringApi] = useSpring(
|
||||
{
|
||||
from: { opacity: 0 },
|
||||
to: { opacity: 1 },
|
||||
config: { clamp: true, friction: 22, tension: 360 },
|
||||
immediate: reducedMotion,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// We show the first pending participant. Save this participant, so if all requests
|
||||
// are resolved then we can keep showing the participant while fading out.
|
||||
const lastParticipantRef = React.useRef<ConversationType | undefined>();
|
||||
lastParticipantRef.current = participants[0] ?? lastParticipantRef.current;
|
||||
const participantCount = participants.length;
|
||||
const prevParticipantCount = usePrevious(participantCount, participantCount);
|
||||
|
||||
const [isVisible, setIsVisible] = useState(participantCount > 0);
|
||||
const [isExpanded, setIsExpanded] = useState(defaultIsExpanded ?? false);
|
||||
const [confirmDialogState, setConfirmDialogState] =
|
||||
React.useState<ConfirmDialogState>(ConfirmDialogState.None);
|
||||
const [serviceIdsStagedForAction, setServiceIdsStagedForAction] =
|
||||
React.useState<Array<ServiceIdString>>([]);
|
||||
useState<ConfirmDialogState>(ConfirmDialogState.None);
|
||||
const [serviceIdsStagedForAction, setServiceIdsStagedForAction] = useState<
|
||||
Array<ServiceIdString>
|
||||
>([]);
|
||||
|
||||
const expandedListRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
@ -120,7 +147,7 @@ export function CallingPendingParticipants({
|
|||
}, [serviceIdsStagedForAction, batchUserAction, hideConfirmDialog]);
|
||||
|
||||
const renderApprovalButtons = useCallback(
|
||||
(participant: ConversationType) => {
|
||||
(participant: ConversationType, isEnabled: boolean = true) => {
|
||||
if (participant.serviceId == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -130,7 +157,7 @@ export function CallingPendingParticipants({
|
|||
<Button
|
||||
aria-label={i18n('icu:CallingPendingParticipants__DenyUser')}
|
||||
className="CallingPendingParticipants__PendingActionButton CallingButton__icon"
|
||||
onClick={() => handleDeny(participant)}
|
||||
onClick={isEnabled ? () => handleDeny(participant) : noop}
|
||||
variant={ButtonVariant.Destructive}
|
||||
>
|
||||
<span className="CallingPendingParticipants__PendingActionButtonIcon CallingPendingParticipants__PendingActionButtonIcon--Deny" />
|
||||
|
@ -138,7 +165,7 @@ export function CallingPendingParticipants({
|
|||
<Button
|
||||
aria-label={i18n('icu:CallingPendingParticipants__ApproveUser')}
|
||||
className="CallingPendingParticipants__PendingActionButton CallingButton__icon"
|
||||
onClick={() => handleApprove(participant)}
|
||||
onClick={isEnabled ? () => handleApprove(participant) : noop}
|
||||
variant={ButtonVariant.Calling}
|
||||
>
|
||||
<span className="CallingPendingParticipants__PendingActionButtonIcon CallingPendingParticipants__PendingActionButtonIcon--Approve" />
|
||||
|
@ -165,6 +192,32 @@ export function CallingPendingParticipants({
|
|||
);
|
||||
}, [isExpanded, handleHideAllRequests]);
|
||||
|
||||
useEffect(() => {
|
||||
if (participantCount > prevParticipantCount) {
|
||||
setIsVisible(true);
|
||||
opacitySpringApi.stop();
|
||||
drop(Promise.all(opacitySpringApi.start({ opacity: 1 })));
|
||||
} else if (participantCount === 0) {
|
||||
opacitySpringApi.stop();
|
||||
drop(
|
||||
Promise.all(
|
||||
opacitySpringApi.start({
|
||||
to: { opacity: 0 },
|
||||
onRest: () => {
|
||||
if (!participantCount) {
|
||||
setIsVisible(false);
|
||||
}
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [opacitySpringApi, participantCount, prevParticipantCount, setIsVisible]);
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (confirmDialogState === ConfirmDialogState.ApproveAll) {
|
||||
return (
|
||||
<ConfirmationDialog
|
||||
|
@ -228,7 +281,7 @@ export function CallingPendingParticipants({
|
|||
<div className="module-calling-participants-list__header">
|
||||
<div className="module-calling-participants-list__title">
|
||||
{i18n('icu:CallingPendingParticipants__RequestsToJoin', {
|
||||
count: participants.length,
|
||||
count: participantCount,
|
||||
})}
|
||||
</div>
|
||||
<button
|
||||
|
@ -311,15 +364,33 @@ export function CallingPendingParticipants({
|
|||
);
|
||||
}
|
||||
|
||||
const participant = participants[0];
|
||||
const participant = lastParticipantRef.current;
|
||||
if (!participant) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isExpandable = participantCount > 1;
|
||||
return (
|
||||
<div className="CallingPendingParticipants CallingPendingParticipants--Compact module-calling-participants-list">
|
||||
<animated.div
|
||||
className={classNames(
|
||||
'CallingPendingParticipants',
|
||||
'CallingPendingParticipants--Compact',
|
||||
'module-calling-participants-list',
|
||||
isExpandable && 'CallingPendingParticipants--Expandable'
|
||||
)}
|
||||
style={opacitySpringProps}
|
||||
aria-hidden={participantCount === 0}
|
||||
>
|
||||
<div className="CallingPendingParticipants__CompactParticipant">
|
||||
<button
|
||||
type="button"
|
||||
onClick={ev => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
if (participantCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggleCallLinkPendingParticipantModal(participant.id);
|
||||
}}
|
||||
className="module-calling-participants-list__avatar-and-name CallingPendingParticipants__ParticipantButton"
|
||||
|
@ -353,9 +424,9 @@ export function CallingPendingParticipants({
|
|||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{renderApprovalButtons(participant)}
|
||||
{renderApprovalButtons(participant, participantCount > 0)}
|
||||
</div>
|
||||
{participants.length > 1 && (
|
||||
{isExpandable && (
|
||||
<div className="CallingPendingParticipants__ShowAllRequestsButtonContainer">
|
||||
<button
|
||||
className="CallingPendingParticipants__ShowAllRequestsButton"
|
||||
|
@ -363,11 +434,11 @@ export function CallingPendingParticipants({
|
|||
type="button"
|
||||
>
|
||||
{i18n('icu:CallingPendingParticipants__AdditionalRequests', {
|
||||
count: participants.length - 1,
|
||||
count: participantCount - 1,
|
||||
})}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</animated.div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3126,5 +3126,13 @@
|
|||
"line": " message.innerHTML = window.i18n('icu:optimizingApplication');",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2021-09-17T21:02:59.414Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/CallingPendingParticipants.tsx",
|
||||
"line": " const lastParticipantRef = React.useRef<ConversationType | undefined>();",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2024-09-20T02:11:27.851Z",
|
||||
"reasonDetail": "For fading out, to keep showing the last known participant"
|
||||
}
|
||||
]
|
||||
|
|
Loading…
Add table
Reference in a new issue