Animate call link join requests
This commit is contained in:
parent
7e9773a144
commit
e51cde1770
5 changed files with 117 additions and 15 deletions
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue