178 lines
		
	
	
	
		
			4.9 KiB
			
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
	
		
			4.9 KiB
			
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| // Copyright 2020 Signal Messenger, LLC
 | |
| // SPDX-License-Identifier: AGPL-3.0-only
 | |
| 
 | |
| import type { ReactNode } from 'react';
 | |
| import React from 'react';
 | |
| import { noop } from 'lodash';
 | |
| 
 | |
| import { SystemMessage, SystemMessageKind } from './SystemMessage';
 | |
| import { Button, ButtonSize, ButtonVariant } from '../Button';
 | |
| import { MessageTimestamp } from './MessageTimestamp';
 | |
| import type { LocalizerType } from '../../types/Util';
 | |
| import { CallMode } from '../../types/Calling';
 | |
| import type { CallingNotificationType } from '../../util/callingNotification';
 | |
| import {
 | |
|   getCallingIcon,
 | |
|   getCallingNotificationText,
 | |
| } from '../../util/callingNotification';
 | |
| import { missingCaseError } from '../../util/missingCaseError';
 | |
| import { Tooltip, TooltipPlacement } from '../Tooltip';
 | |
| import * as log from '../../logging/log';
 | |
| import { assertDev } from '../../util/assert';
 | |
| 
 | |
| export type PropsActionsType = {
 | |
|   returnToActiveCall: () => void;
 | |
|   startCallingLobby: (_: {
 | |
|     conversationId: string;
 | |
|     isVideoCall: boolean;
 | |
|   }) => void;
 | |
| };
 | |
| 
 | |
| type PropsHousekeeping = {
 | |
|   i18n: LocalizerType;
 | |
|   conversationId: string;
 | |
|   isNextItemCallingNotification: boolean;
 | |
| };
 | |
| 
 | |
| type PropsType = CallingNotificationType & PropsActionsType & PropsHousekeeping;
 | |
| 
 | |
| export const CallingNotification: React.FC<PropsType> = React.memo(
 | |
|   function CallingNotificationInner(props) {
 | |
|     const { i18n } = props;
 | |
| 
 | |
|     let timestamp: number;
 | |
|     let wasMissed = false;
 | |
|     switch (props.callMode) {
 | |
|       case CallMode.Direct: {
 | |
|         const resolvedTime = props.acceptedTime ?? props.endedTime;
 | |
|         assertDev(resolvedTime, 'Direct call must have accepted or ended time');
 | |
|         timestamp = resolvedTime;
 | |
|         wasMissed =
 | |
|           props.wasIncoming && !props.acceptedTime && !props.wasDeclined;
 | |
|         break;
 | |
|       }
 | |
|       case CallMode.Group:
 | |
|         timestamp = props.startedTime;
 | |
|         break;
 | |
|       default:
 | |
|         log.error(
 | |
|           `CallingNotification missing case: ${missingCaseError(props)}`
 | |
|         );
 | |
|         return null;
 | |
|     }
 | |
| 
 | |
|     const icon = getCallingIcon(props);
 | |
| 
 | |
|     return (
 | |
|       <SystemMessage
 | |
|         button={renderCallingNotificationButton(props)}
 | |
|         contents={
 | |
|           <>
 | |
|             {getCallingNotificationText(props, i18n)} ·{' '}
 | |
|             <MessageTimestamp
 | |
|               direction="outgoing"
 | |
|               i18n={i18n}
 | |
|               timestamp={timestamp}
 | |
|               withImageNoCaption={false}
 | |
|               withSticker={false}
 | |
|               withTapToViewExpired={false}
 | |
|             />
 | |
|           </>
 | |
|         }
 | |
|         icon={icon}
 | |
|         kind={wasMissed ? SystemMessageKind.Danger : SystemMessageKind.Normal}
 | |
|       />
 | |
|     );
 | |
|   }
 | |
| );
 | |
| 
 | |
| function renderCallingNotificationButton(
 | |
|   props: Readonly<PropsType>
 | |
| ): ReactNode {
 | |
|   const {
 | |
|     activeCallConversationId,
 | |
|     conversationId,
 | |
|     i18n,
 | |
|     isNextItemCallingNotification,
 | |
|     returnToActiveCall,
 | |
|     startCallingLobby,
 | |
|   } = props;
 | |
| 
 | |
|   if (isNextItemCallingNotification) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   let buttonText: string;
 | |
|   let disabledTooltipText: undefined | string;
 | |
|   let onClick: () => void;
 | |
| 
 | |
|   switch (props.callMode) {
 | |
|     case CallMode.Direct: {
 | |
|       const { wasIncoming, wasVideoCall } = props;
 | |
|       buttonText = wasIncoming
 | |
|         ? i18n('calling__call-back')
 | |
|         : i18n('calling__call-again');
 | |
|       if (activeCallConversationId) {
 | |
|         disabledTooltipText = i18n('calling__in-another-call-tooltip');
 | |
|         onClick = noop;
 | |
|       } else {
 | |
|         onClick = () => {
 | |
|           startCallingLobby({ conversationId, isVideoCall: wasVideoCall });
 | |
|         };
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case CallMode.Group: {
 | |
|       if (props.ended) {
 | |
|         return null;
 | |
|       }
 | |
|       const { deviceCount, maxDevices } = props;
 | |
|       if (activeCallConversationId) {
 | |
|         if (activeCallConversationId === conversationId) {
 | |
|           buttonText = i18n('calling__return');
 | |
|           onClick = returnToActiveCall;
 | |
|         } else {
 | |
|           buttonText = i18n('calling__join');
 | |
|           disabledTooltipText = i18n('calling__in-another-call-tooltip');
 | |
|           onClick = noop;
 | |
|         }
 | |
|       } else if (deviceCount >= maxDevices) {
 | |
|         buttonText = i18n('calling__call-is-full');
 | |
|         disabledTooltipText = i18n(
 | |
|           'calling__call-notification__button__call-full-tooltip',
 | |
|           [String(deviceCount)]
 | |
|         );
 | |
|         onClick = noop;
 | |
|       } else {
 | |
|         buttonText = i18n('calling__join');
 | |
|         onClick = () => {
 | |
|           startCallingLobby({ conversationId, isVideoCall: true });
 | |
|         };
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     default:
 | |
|       log.error(missingCaseError(props));
 | |
|       return null;
 | |
|   }
 | |
| 
 | |
|   const button = (
 | |
|     <Button
 | |
|       disabled={Boolean(disabledTooltipText)}
 | |
|       onClick={onClick}
 | |
|       size={ButtonSize.Small}
 | |
|       variant={ButtonVariant.SystemMessage}
 | |
|     >
 | |
|       {buttonText}
 | |
|     </Button>
 | |
|   );
 | |
| 
 | |
|   if (disabledTooltipText) {
 | |
|     return (
 | |
|       <Tooltip content={disabledTooltipText} direction={TooltipPlacement.Top}>
 | |
|         {button}
 | |
|       </Tooltip>
 | |
|     );
 | |
|   }
 | |
|   return button;
 | |
| }
 | 
