signal-desktop/ts/components/CallingParticipantsList.tsx

155 lines
5.3 KiB
TypeScript
Raw Normal View History

// Copyright 2020-2021 Signal Messenger, LLC
2020-10-30 20:34:04 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
2020-10-15 19:53:21 +00:00
/* eslint-disable react/no-array-index-key */
import React from 'react';
import { createPortal } from 'react-dom';
import { Avatar } from './Avatar';
import { ContactName } from './conversation/ContactName';
2020-11-20 19:39:50 +00:00
import { InContactsIcon } from './InContactsIcon';
import type { LocalizerType } from '../types/Util';
import { sortByTitle } from '../util/sortByTitle';
import type { ConversationType } from '../state/ducks/conversations';
import { isInSystemContacts } from '../util/isInSystemContacts';
2020-12-02 18:14:03 +00:00
type ParticipantType = ConversationType & {
hasRemoteAudio?: boolean;
hasRemoteVideo?: boolean;
presenting?: boolean;
};
2020-10-15 19:53:21 +00:00
export type PropsType = {
readonly i18n: LocalizerType;
readonly onClose: () => void;
readonly ourUuid: string;
2020-12-02 18:14:03 +00:00
readonly participants: Array<ParticipantType>;
2020-10-15 19:53:21 +00:00
};
export const CallingParticipantsList = React.memo(
({ i18n, onClose, ourUuid, participants }: PropsType) => {
2020-10-15 19:53:21 +00:00
const [root, setRoot] = React.useState<HTMLElement | null>(null);
2020-12-02 18:14:03 +00:00
const sortedParticipants = React.useMemo<Array<ParticipantType>>(
() => sortByTitle(participants),
2020-12-02 18:14:03 +00:00
[participants]
);
2020-10-15 19:53:21 +00:00
React.useEffect(() => {
const div = document.createElement('div');
document.body.appendChild(div);
setRoot(div);
return () => {
document.body.removeChild(div);
setRoot(null);
};
}, []);
2020-11-20 19:39:50 +00:00
const handleCancel = React.useCallback(
(e: React.MouseEvent) => {
if (e.target === e.currentTarget) {
onClose();
}
},
[onClose]
);
2020-10-15 19:53:21 +00:00
if (!root) {
return null;
}
return createPortal(
<div
className="module-calling-participants-list__overlay"
2020-11-20 19:39:50 +00:00
onClick={handleCancel}
role="presentation"
2020-10-15 19:53:21 +00:00
>
<div className="module-calling-participants-list">
<div className="module-calling-participants-list__header">
<div className="module-calling-participants-list__title">
2020-11-17 15:07:53 +00:00
{!participants.length && i18n('calling__in-this-call--zero')}
{participants.length === 1 && i18n('calling__in-this-call--one')}
{participants.length > 1 &&
i18n('calling__in-this-call--many', [
String(participants.length),
])}
2020-10-15 19:53:21 +00:00
</div>
<button
type="button"
className="module-calling-participants-list__close"
onClick={onClose}
tabIndex={0}
aria-label={i18n('close')}
/>
</div>
<ul className="module-calling-participants-list__list">
2020-12-02 18:14:03 +00:00
{sortedParticipants.map(
(participant: ParticipantType, index: number) => (
2020-11-17 15:07:53 +00:00
<li
className="module-calling-participants-list__contact"
2020-12-02 18:14:03 +00:00
// It's tempting to use `participant.uuid` as the `key` here, but that
// can result in duplicate keys for participants who have joined on
// multiple devices.
2020-11-17 15:07:53 +00:00
key={index}
>
<div>
<Avatar
2021-05-07 22:21:10 +00:00
acceptedMessageRequest={
participant.acceptedMessageRequest
}
2020-11-17 15:07:53 +00:00
avatarPath={participant.avatarPath}
badge={undefined}
2020-11-17 15:07:53 +00:00
color={participant.color}
conversationType="direct"
i18n={i18n}
2021-05-07 22:21:10 +00:00
isMe={participant.isMe}
2020-11-17 15:07:53 +00:00
profileName={participant.profileName}
title={participant.title}
2021-05-07 22:21:10 +00:00
sharedGroupNames={participant.sharedGroupNames}
2020-11-17 15:07:53 +00:00
size={32}
/>
{participant.uuid === ourUuid ? (
2020-11-17 15:07:53 +00:00
<span className="module-calling-participants-list__name">
{i18n('you')}
</span>
) : (
2020-11-20 19:39:50 +00:00
<>
<ContactName
module="module-calling-participants-list__name"
title={participant.title}
/>
{isInSystemContacts(participant) ? (
2020-11-20 19:39:50 +00:00
<span>
{' '}
<InContactsIcon
className="module-calling-participants-list__contact-icon"
i18n={i18n}
/>
</span>
) : null}
</>
2020-11-17 15:07:53 +00:00
)}
</div>
<div>
{participant.hasRemoteAudio === false ? (
2020-11-17 15:07:53 +00:00
<span className="module-calling-participants-list__muted--audio" />
) : null}
{participant.hasRemoteVideo === false ? (
2020-11-17 15:07:53 +00:00
<span className="module-calling-participants-list__muted--video" />
) : null}
{participant.presenting ? (
<span className="module-calling-participants-list__presenting" />
) : null}
2020-11-17 15:07:53 +00:00
</div>
</li>
)
)}
2020-10-15 19:53:21 +00:00
</ul>
</div>
</div>,
root
);
}
);