signal-desktop/ts/components/conversation/ConversationHero.tsx

207 lines
5.5 KiB
TypeScript
Raw Normal View History

2020-10-30 20:34:04 +00:00
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
2020-05-27 21:37:06 +00:00
import * as React from 'react';
import { take } from 'lodash';
import { Avatar, Props as AvatarProps } from '../Avatar';
import { ContactName } from './ContactName';
import { Emojify } from './Emojify';
import { Intl } from '../Intl';
import { LocalizerType } from '../../types/Util';
export type Props = {
i18n: LocalizerType;
isMe?: boolean;
2020-08-05 01:13:19 +00:00
sharedGroupNames?: Array<string>;
2020-05-27 21:37:06 +00:00
membersCount?: number;
2020-07-24 01:35:32 +00:00
phoneNumber?: string;
2020-05-27 21:37:06 +00:00
onHeightChange?: () => unknown;
2020-08-07 00:50:54 +00:00
updateSharedGroups?: () => unknown;
2020-05-27 21:37:06 +00:00
} & Omit<AvatarProps, 'onClick' | 'size' | 'noteToSelf'>;
const renderMembershipRow = ({
i18n,
2020-08-05 01:13:19 +00:00
sharedGroupNames,
2020-05-27 21:37:06 +00:00
conversationType,
isMe,
2020-08-05 01:13:19 +00:00
}: Pick<Props, 'i18n' | 'sharedGroupNames' | 'conversationType' | 'isMe'>) => {
2020-05-27 21:37:06 +00:00
const className = 'module-conversation-hero__membership';
const nameClassName = `${className}__name`;
if (isMe) {
return <div className={className}>{i18n('noteToSelfHero')}</div>;
}
2020-08-05 01:13:19 +00:00
if (
conversationType === 'direct' &&
sharedGroupNames &&
sharedGroupNames.length > 0
) {
const firstThreeGroups = take(sharedGroupNames, 3).map((group, i) => (
2020-09-14 19:51:27 +00:00
// We cannot guarantee uniqueness of group names
// eslint-disable-next-line react/no-array-index-key
2020-05-27 21:37:06 +00:00
<strong key={i} className={nameClassName}>
<Emojify text={group} />
</strong>
));
2020-08-05 01:13:19 +00:00
if (sharedGroupNames.length > 3) {
const remainingCount = sharedGroupNames.length - 3;
return (
<div className={className}>
<Intl
i18n={i18n}
id="ConversationHero--membership-extra"
components={{
group1: firstThreeGroups[0],
group2: firstThreeGroups[1],
group3: firstThreeGroups[2],
remainingCount: remainingCount.toString(),
}}
/>
</div>
);
2020-09-14 19:51:27 +00:00
}
if (firstThreeGroups.length === 3) {
return (
<div className={className}>
<Intl
i18n={i18n}
id="ConversationHero--membership-3"
components={{
group1: firstThreeGroups[0],
group2: firstThreeGroups[1],
group3: firstThreeGroups[2],
}}
/>
</div>
);
2020-09-14 19:51:27 +00:00
}
if (firstThreeGroups.length >= 2) {
return (
<div className={className}>
<Intl
i18n={i18n}
id="ConversationHero--membership-2"
components={{
group1: firstThreeGroups[0],
group2: firstThreeGroups[1],
}}
/>
</div>
);
2020-09-14 19:51:27 +00:00
}
if (firstThreeGroups.length >= 1) {
return (
<div className={className}>
<Intl
i18n={i18n}
id="ConversationHero--membership-1"
components={{
group: firstThreeGroups[0],
}}
/>
</div>
);
}
2020-05-27 21:37:06 +00:00
}
return null;
};
export const ConversationHero = ({
i18n,
avatarPath,
color,
conversationType,
isMe,
membersCount,
2020-08-05 01:13:19 +00:00
sharedGroupNames = [],
2020-05-27 21:37:06 +00:00
name,
phoneNumber,
profileName,
2020-07-24 01:35:32 +00:00
title,
2020-05-27 21:37:06 +00:00
onHeightChange,
2020-08-07 00:50:54 +00:00
updateSharedGroups,
2020-09-14 19:51:27 +00:00
}: Props): JSX.Element => {
2020-05-27 21:37:06 +00:00
const firstRenderRef = React.useRef(true);
2020-09-14 19:51:27 +00:00
// TODO: DESKTOP-686
/* eslint-disable react-hooks/exhaustive-deps */
2020-05-27 21:37:06 +00:00
React.useEffect(() => {
// If any of the depenencies for this hook change then the height of this
// component may have changed. The cleanup function notifies listeners of
// any potential height changes.
return () => {
2020-08-07 00:50:54 +00:00
// Kick off the expensive hydration of the current sharedGroupNames
if (updateSharedGroups) {
updateSharedGroups();
}
2020-05-27 21:37:06 +00:00
if (onHeightChange && !firstRenderRef.current) {
onHeightChange();
} else {
firstRenderRef.current = false;
}
};
}, [
firstRenderRef,
onHeightChange,
// Avoid collisions in these dependencies by prefixing them
// These dependencies may be dynamic, and therefore may cause height changes
`mc-${membersCount}`,
`n-${name}`,
`pn-${profileName}`,
2020-08-07 00:50:54 +00:00
sharedGroupNames.map(g => `g-${g}`).join(' '),
2020-05-27 21:37:06 +00:00
]);
2020-09-14 19:51:27 +00:00
/* eslint-enable react-hooks/exhaustive-deps */
2020-05-27 21:37:06 +00:00
2020-07-24 01:35:32 +00:00
const phoneNumberOnly = Boolean(
!name && !profileName && conversationType === 'direct'
);
2020-09-14 19:51:27 +00:00
/* eslint-disable no-nested-ternary */
2020-05-27 21:37:06 +00:00
return (
<div className="module-conversation-hero">
<Avatar
i18n={i18n}
color={color}
noteToSelf={isMe}
avatarPath={avatarPath}
conversationType={conversationType}
name={name}
profileName={profileName}
2020-07-24 01:35:32 +00:00
title={title}
2020-05-27 21:37:06 +00:00
size={112}
className="module-conversation-hero__avatar"
/>
<h1 className="module-conversation-hero__profile-name">
{isMe ? (
i18n('noteToSelf')
) : (
<ContactName
2020-07-24 01:35:32 +00:00
title={title}
name={name}
2020-05-27 21:37:06 +00:00
profileName={profileName}
phoneNumber={phoneNumber}
2020-07-24 01:35:32 +00:00
i18n={i18n}
2020-05-27 21:37:06 +00:00
/>
)}
</h1>
{!isMe ? (
<div className="module-conversation-hero__with">
{membersCount === 1
? i18n('ConversationHero--members-1')
: membersCount !== undefined
? i18n('ConversationHero--members', [`${membersCount}`])
2020-07-24 01:35:32 +00:00
: phoneNumberOnly
? null
2020-05-27 21:37:06 +00:00
: phoneNumber}
</div>
) : null}
2020-08-05 01:13:19 +00:00
{renderMembershipRow({ isMe, sharedGroupNames, conversationType, i18n })}
2020-05-27 21:37:06 +00:00
</div>
);
2020-09-14 19:51:27 +00:00
/* eslint-enable no-nested-ternary */
2020-05-27 21:37:06 +00:00
};