| 
									
										
										
										
											2023-01-03 11:55:46 -08:00
										 |  |  | // Copyright 2018 Signal Messenger, LLC
 | 
					
						
							| 
									
										
										
										
											2020-10-30 15:34:04 -05:00
										 |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { ReactNode } from 'react'; | 
					
						
							|  |  |  | import React from 'react'; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  | import { compact, flatten } from 'lodash'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import { ContactName } from './ContactName'; | 
					
						
							| 
									
										
										
										
											2021-09-07 14:55:03 -05:00
										 |  |  | import { SystemMessage } from './SystemMessage'; | 
					
						
							| 
									
										
										
										
											2020-06-11 10:32:21 -07:00
										 |  |  | import { Intl } from '../Intl'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { LocalizerType } from '../../types/Util'; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import { missingCaseError } from '../../util/missingCaseError'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { ConversationType } from '../../state/ducks/conversations'; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 13:57:54 -07:00
										 |  |  | export type ChangeType = 'add' | 'remove' | 'name' | 'avatar' | 'general'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-14 12:07:05 -06:00
										 |  |  | type Change = { | 
					
						
							| 
									
										
										
										
											2020-09-24 13:57:54 -07:00
										 |  |  |   type: ChangeType; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |   newName?: string; | 
					
						
							| 
									
										
										
										
											2021-09-17 13:57:54 -07:00
										 |  |  |   contacts?: Array<ConversationType>; | 
					
						
							| 
									
										
										
										
											2021-01-14 12:07:05 -06:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | export type PropsData = { | 
					
						
							| 
									
										
										
										
											2021-09-17 13:57:54 -07:00
										 |  |  |   from: ConversationType; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |   changes: Array<Change>; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type PropsHousekeeping = { | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |   i18n: LocalizerType; | 
					
						
							| 
									
										
										
										
											2019-03-20 10:42:28 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  | export type Props = PropsData & PropsHousekeeping; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export class GroupNotification extends React.Component<Props> { | 
					
						
							| 
									
										
										
										
											2020-09-14 12:51:27 -07:00
										 |  |  |   public renderChange( | 
					
						
							|  |  |  |     change: Change, | 
					
						
							| 
									
										
										
										
											2021-09-17 13:57:54 -07:00
										 |  |  |     from: ConversationType | 
					
						
							| 
									
										
										
										
											2020-09-14 12:51:27 -07:00
										 |  |  |   ): JSX.Element | string | null | undefined { | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |     const { contacts, type, newName } = change; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |     const { i18n } = this.props; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-11 10:32:21 -07:00
										 |  |  |     const otherPeople: Array<JSX.Element> = compact( | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |       (contacts || []).map(contact => { | 
					
						
							|  |  |  |         if (contact.isMe) { | 
					
						
							|  |  |  |           return null; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |         return ( | 
					
						
							|  |  |  |           <span | 
					
						
							| 
									
										
										
										
											2021-09-17 13:57:54 -07:00
										 |  |  |             key={`external-${contact.id}`} | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |             className="module-group-notification__contact" | 
					
						
							|  |  |  |           > | 
					
						
							| 
									
										
										
										
											2021-09-16 11:15:43 -05:00
										 |  |  |             <ContactName title={contact.title} /> | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |           </span> | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-06-11 10:32:21 -07:00
										 |  |  |     const otherPeopleWithCommas: Array<JSX.Element | string> = compact( | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |       flatten( | 
					
						
							|  |  |  |         otherPeople.map((person, index) => [index > 0 ? ', ' : null, person]) | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |       ) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |     const contactsIncludesMe = (contacts || []).length !== otherPeople.length; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     switch (type) { | 
					
						
							|  |  |  |       case 'name': | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |         return ( | 
					
						
							|  |  |  |           <Intl i18n={i18n} id="titleIsNow" components={[newName || '']} /> | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       case 'avatar': | 
					
						
							|  |  |  |         return <Intl i18n={i18n} id="updatedGroupAvatar" />; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |       case 'add': | 
					
						
							|  |  |  |         if (!contacts || !contacts.length) { | 
					
						
							|  |  |  |           throw new Error('Group update is missing contacts'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-26 00:36:24 +02:00
										 |  |  |         return ( | 
					
						
							|  |  |  |           <> | 
					
						
							|  |  |  |             {otherPeople.length > 0 && ( | 
					
						
							| 
									
										
										
										
											2023-01-05 14:43:33 -08:00
										 |  |  |               <> | 
					
						
							|  |  |  |                 {otherPeople.length === 1 ? ( | 
					
						
							|  |  |  |                   <Intl | 
					
						
							|  |  |  |                     i18n={i18n} | 
					
						
							|  |  |  |                     id="joinedTheGroup" | 
					
						
							|  |  |  |                     components={[otherPeopleWithCommas]} | 
					
						
							|  |  |  |                   /> | 
					
						
							|  |  |  |                 ) : ( | 
					
						
							|  |  |  |                   <Intl | 
					
						
							|  |  |  |                     i18n={i18n} | 
					
						
							|  |  |  |                     id="multipleJoinedTheGroup" | 
					
						
							|  |  |  |                     components={[otherPeopleWithCommas]} | 
					
						
							|  |  |  |                   /> | 
					
						
							|  |  |  |                 )} | 
					
						
							|  |  |  |               </> | 
					
						
							| 
									
										
										
										
											2020-06-26 00:36:24 +02:00
										 |  |  |             )} | 
					
						
							|  |  |  |             {contactsIncludesMe && ( | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |               <div className="module-group-notification__change"> | 
					
						
							|  |  |  |                 <Intl i18n={i18n} id="youJoinedTheGroup" /> | 
					
						
							|  |  |  |               </div> | 
					
						
							| 
									
										
										
										
											2020-06-26 00:36:24 +02:00
										 |  |  |             )} | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |           </> | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |       case 'remove': | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |         if (from && from.isMe) { | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |           return i18n('youLeftTheGroup'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-13 12:57:07 -07:00
										 |  |  |         if (!contacts || !contacts.length) { | 
					
						
							|  |  |  |           throw new Error('Group update is missing contacts'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-05 14:43:33 -08:00
										 |  |  |         return contacts.length > 1 ? ( | 
					
						
							|  |  |  |           <Intl | 
					
						
							|  |  |  |             id="multipleLeftTheGroup" | 
					
						
							|  |  |  |             i18n={i18n} | 
					
						
							|  |  |  |             components={[otherPeopleWithCommas]} | 
					
						
							|  |  |  |           /> | 
					
						
							|  |  |  |         ) : ( | 
					
						
							|  |  |  |           <Intl | 
					
						
							|  |  |  |             id="leftTheGroup" | 
					
						
							|  |  |  |             i18n={i18n} | 
					
						
							|  |  |  |             components={[otherPeopleWithCommas]} | 
					
						
							|  |  |  |           /> | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |       case 'general': | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |       default: | 
					
						
							|  |  |  |         throw missingCaseError(type); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 17:44:20 -06:00
										 |  |  |   public override render(): JSX.Element { | 
					
						
							| 
									
										
										
										
											2021-09-07 14:55:03 -05:00
										 |  |  |     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 : []; | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Leave messages are always from the person leaving, so we omit the fromLabel if
 | 
					
						
							|  |  |  |     //   the change is a 'leave.'
 | 
					
						
							| 
									
										
										
										
											2021-09-07 14:55:03 -05:00
										 |  |  |     const firstChange: undefined | Change = changes[0]; | 
					
						
							|  |  |  |     const isLeftOnly = changes.length === 1 && firstChange?.type === 'remove'; | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const fromLabel = from.isMe ? ( | 
					
						
							|  |  |  |       <Intl i18n={i18n} id="youUpdatedTheGroup" /> | 
					
						
							|  |  |  |     ) : ( | 
					
						
							| 
									
										
										
										
											2021-09-16 11:15:43 -05:00
										 |  |  |       <Intl | 
					
						
							|  |  |  |         i18n={i18n} | 
					
						
							|  |  |  |         id="updatedTheGroup" | 
					
						
							|  |  |  |         components={[<ContactName title={from.title} />]} | 
					
						
							|  |  |  |       /> | 
					
						
							| 
									
										
										
										
											2020-03-26 14:47:35 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-07 14:55:03 -05:00
										 |  |  |     let contents: ReactNode; | 
					
						
							|  |  |  |     if (isLeftOnly) { | 
					
						
							|  |  |  |       contents = this.renderChange(firstChange, from); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       contents = ( | 
					
						
							|  |  |  |         <> | 
					
						
							|  |  |  |           <p>{fromLabel}</p> | 
					
						
							|  |  |  |           {changes.map((change, i) => ( | 
					
						
							| 
									
										
										
										
											2021-08-26 16:51:55 -04:00
										 |  |  |             // eslint-disable-next-line react/no-array-index-key
 | 
					
						
							| 
									
										
										
										
											2021-09-07 14:55:03 -05:00
										 |  |  |             <p key={i} className="module-group-notification__change"> | 
					
						
							| 
									
										
										
										
											2021-08-26 16:51:55 -04:00
										 |  |  |               {this.renderChange(change, from)} | 
					
						
							| 
									
										
										
										
											2021-09-07 14:55:03 -05:00
										 |  |  |             </p> | 
					
						
							| 
									
										
										
										
											2021-08-26 16:51:55 -04:00
										 |  |  |           ))} | 
					
						
							| 
									
										
										
										
											2021-09-07 14:55:03 -05:00
										 |  |  |         </> | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return <SystemMessage contents={contents} icon="group" />; | 
					
						
							| 
									
										
										
										
											2018-07-09 14:29:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } |