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

183 lines
5 KiB
TypeScript
Raw Normal View History

// Copyright 2018-2021 Signal Messenger, LLC
2020-10-30 20:34:04 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
import React, { ReactNode } from 'react';
import { compact, flatten } from 'lodash';
import { ContactName } from './ContactName';
import { SystemMessage } from './SystemMessage';
import { Intl } from '../Intl';
2019-01-14 21:49:58 +00:00
import { LocalizerType } from '../../types/Util';
import { missingCaseError } from '../../util/missingCaseError';
type Contact = {
2020-07-24 01:35:32 +00:00
phoneNumber?: string;
profileName?: string;
name?: string;
2020-07-24 01:35:32 +00:00
title: string;
isMe?: boolean;
};
export type ChangeType = 'add' | 'remove' | 'name' | 'avatar' | 'general';
type Change = {
type: ChangeType;
newName?: string;
contacts?: Array<Contact>;
};
export type PropsData = {
from: Contact;
changes: Array<Change>;
};
type PropsHousekeeping = {
2019-01-14 21:49:58 +00:00
i18n: LocalizerType;
};
export type Props = PropsData & PropsHousekeeping;
export class GroupNotification extends React.Component<Props> {
2020-09-14 19:51:27 +00:00
public renderChange(
change: Change,
from: Contact
): JSX.Element | string | null | undefined {
const { contacts, type, newName } = change;
const { i18n } = this.props;
const otherPeople: Array<JSX.Element> = compact(
(contacts || []).map(contact => {
if (contact.isMe) {
return null;
}
return (
<span
key={`external-${contact.phoneNumber}`}
className="module-group-notification__contact"
>
<ContactName
2020-07-24 01:35:32 +00:00
title={contact.title}
phoneNumber={contact.phoneNumber}
profileName={contact.profileName}
name={contact.name}
2020-07-24 01:35:32 +00:00
i18n={i18n}
/>
</span>
);
})
);
const otherPeopleWithCommas: Array<JSX.Element | string> = compact(
flatten(
otherPeople.map((person, index) => [index > 0 ? ', ' : null, person])
)
);
const contactsIncludesMe = (contacts || []).length !== otherPeople.length;
switch (type) {
case 'name':
return (
<Intl i18n={i18n} id="titleIsNow" components={[newName || '']} />
);
case 'avatar':
return <Intl i18n={i18n} id="updatedGroupAvatar" />;
case 'add':
if (!contacts || !contacts.length) {
throw new Error('Group update is missing contacts');
}
2020-09-14 19:51:27 +00:00
// eslint-disable-next-line no-case-declarations
const otherPeopleNotifMsg =
otherPeople.length === 1
? 'joinedTheGroup'
: 'multipleJoinedTheGroup';
return (
<>
{otherPeople.length > 0 && (
<Intl
i18n={i18n}
id={otherPeopleNotifMsg}
components={[otherPeopleWithCommas]}
/>
)}
{contactsIncludesMe && (
<div className="module-group-notification__change">
<Intl i18n={i18n} id="youJoinedTheGroup" />
</div>
)}
</>
);
case 'remove':
if (from && from.isMe) {
return i18n('youLeftTheGroup');
}
if (!contacts || !contacts.length) {
throw new Error('Group update is missing contacts');
}
2020-09-14 19:51:27 +00:00
// eslint-disable-next-line no-case-declarations
2019-01-14 21:49:58 +00:00
const leftKey =
contacts.length > 1 ? 'multipleLeftTheGroup' : 'leftTheGroup';
return (
<Intl i18n={i18n} id={leftKey} components={[otherPeopleWithCommas]} />
);
case 'general':
2020-09-14 19:51:27 +00:00
// eslint-disable-next-line consistent-return
return;
default:
throw missingCaseError(type);
}
}
2020-09-14 19:51:27 +00:00
public render(): JSX.Element {
const { changes: rawChanges, i18n, from } = this.props;
// This check is just to be extra careful, and can probably be removed.
const changes: Array<Change> = Array.isArray(rawChanges) ? rawChanges : [];
// Leave messages are always from the person leaving, so we omit the fromLabel if
// the change is a 'leave.'
const firstChange: undefined | Change = changes[0];
const isLeftOnly = changes.length === 1 && firstChange?.type === 'remove';
const fromContact = (
<ContactName
2020-07-24 01:35:32 +00:00
title={from.title}
phoneNumber={from.phoneNumber}
profileName={from.profileName}
name={from.name}
2020-07-24 01:35:32 +00:00
i18n={i18n}
/>
);
const fromLabel = from.isMe ? (
<Intl i18n={i18n} id="youUpdatedTheGroup" />
) : (
<Intl i18n={i18n} id="updatedTheGroup" components={[fromContact]} />
);
let contents: ReactNode;
if (isLeftOnly) {
contents = this.renderChange(firstChange, from);
} else {
contents = (
<>
<p>{fromLabel}</p>
{changes.map((change, i) => (
2021-08-26 20:51:55 +00:00
// eslint-disable-next-line react/no-array-index-key
<p key={i} className="module-group-notification__change">
2021-08-26 20:51:55 +00:00
{this.renderChange(change, from)}
</p>
2021-08-26 20:51:55 +00:00
))}
</>
);
}
return <SystemMessage contents={contents} icon="group" />;
}
}