You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			176 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			176 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			TypeScript
		
	
| import React from 'react';
 | |
| import styled from 'styled-components';
 | |
| import { useDispatch, useSelector } from 'react-redux';
 | |
| import useKey from 'react-use/lib/useKey';
 | |
| import { SessionIcon, SessionIconButton } from '../icon';
 | |
| 
 | |
| import { quoteMessage } from '../../state/ducks/conversations';
 | |
| import { getQuotedMessage } from '../../state/selectors/conversations';
 | |
| import { getAlt, isAudio } from '../../types/Attachment';
 | |
| import { AUDIO_MP3 } from '../../types/MIME';
 | |
| import { Flex } from '../basic/Flex';
 | |
| import { Image } from './Image';
 | |
| 
 | |
| import { getAbsoluteAttachmentPath } from '../../types/MessageAttachment';
 | |
| import { GoogleChrome } from '../../util';
 | |
| import { findAndFormatContact } from '../../models/message';
 | |
| 
 | |
| const QuotedMessageComposition = styled(Flex)`
 | |
|   border-top: 1px solid var(--border-color);
 | |
| `;
 | |
| 
 | |
| const QuotedMessageCompositionReply = styled(Flex)<{ hasAttachments: boolean }>`
 | |
|   ${props => !props.hasAttachments && 'border-left: 3px solid var(--primary-color);'}
 | |
| `;
 | |
| 
 | |
| const Subtle = styled.div`
 | |
|   overflow: hidden;
 | |
|   text-overflow: ellipsis;
 | |
|   word-break: break-all;
 | |
|   -webkit-line-clamp: 1;
 | |
|   -webkit-box-orient: vertical;
 | |
|   display: -webkit-box;
 | |
|   color: var(--text-primary-color);
 | |
| `;
 | |
| 
 | |
| const StyledImage = styled.div`
 | |
|   div {
 | |
|     border-radius: 4px;
 | |
|     overflow: hidden;
 | |
|   }
 | |
| `;
 | |
| 
 | |
| const StyledText = styled(Flex)`
 | |
|   margin: 0 0 0 var(--margins-sm);
 | |
|   p {
 | |
|     font-weight: bold;
 | |
|     margin: 0;
 | |
|   }
 | |
| `;
 | |
| 
 | |
| function checkHasAttachments(attachments: Array<any> | undefined) {
 | |
|   const hasAttachments = attachments && attachments.length > 0 && attachments[0];
 | |
| 
 | |
|   // NOTE could be a video as well which will load a thumbnail
 | |
|   const firstImageLikeAttachment =
 | |
|     hasAttachments && attachments[0].contentType !== AUDIO_MP3 && attachments[0].thumbnail
 | |
|       ? attachments[0]
 | |
|       : undefined;
 | |
| 
 | |
|   return { hasAttachments, firstImageLikeAttachment };
 | |
| }
 | |
| 
 | |
| function renderSubtitleText(
 | |
|   quoteText: string | undefined,
 | |
|   hasAudioAttachment: boolean,
 | |
|   isGenericFile: boolean,
 | |
|   isVideo: boolean,
 | |
|   isImage: boolean
 | |
| ): string | null {
 | |
|   return quoteText && quoteText !== ''
 | |
|     ? quoteText
 | |
|     : hasAudioAttachment
 | |
|     ? window.i18n('audio')
 | |
|     : isGenericFile
 | |
|     ? window.i18n('document')
 | |
|     : isVideo
 | |
|     ? window.i18n('video')
 | |
|     : isImage
 | |
|     ? window.i18n('image')
 | |
|     : null;
 | |
| }
 | |
| 
 | |
| export const SessionQuotedMessageComposition = () => {
 | |
|   const dispatch = useDispatch();
 | |
|   const quotedMessageProps = useSelector(getQuotedMessage);
 | |
| 
 | |
|   const { author, attachments, text: quoteText } = quotedMessageProps || {};
 | |
| 
 | |
|   const removeQuotedMessage = () => {
 | |
|     dispatch(quoteMessage(undefined));
 | |
|   };
 | |
| 
 | |
|   useKey('Escape', removeQuotedMessage, undefined, []);
 | |
| 
 | |
|   if (!author || !quotedMessageProps?.id) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   const contact = findAndFormatContact(author);
 | |
|   const authorName = contact?.profileName || contact?.name || author || window.i18n('unknown');
 | |
| 
 | |
|   const { hasAttachments, firstImageLikeAttachment } = checkHasAttachments(attachments);
 | |
|   const isImage = Boolean(
 | |
|     firstImageLikeAttachment &&
 | |
|       GoogleChrome.isImageTypeSupported(firstImageLikeAttachment.contentType)
 | |
|   );
 | |
|   const isVideo = Boolean(
 | |
|     firstImageLikeAttachment &&
 | |
|       GoogleChrome.isVideoTypeSupported(firstImageLikeAttachment.contentType)
 | |
|   );
 | |
|   const hasAudioAttachment = Boolean(hasAttachments && isAudio(attachments));
 | |
|   const isGenericFile = !hasAudioAttachment && !isVideo && !isImage;
 | |
| 
 | |
|   const subtitleText = renderSubtitleText(
 | |
|     quoteText,
 | |
|     hasAudioAttachment,
 | |
|     isGenericFile,
 | |
|     isVideo,
 | |
|     isImage
 | |
|   );
 | |
| 
 | |
|   return (
 | |
|     <QuotedMessageComposition
 | |
|       container={true}
 | |
|       justifyContent="space-between"
 | |
|       alignItems="center"
 | |
|       width={'100%'}
 | |
|       flexGrow={1}
 | |
|       padding={'var(--margins-md)'}
 | |
|     >
 | |
|       <QuotedMessageCompositionReply
 | |
|         container={true}
 | |
|         justifyContent="flex-start"
 | |
|         alignItems={'center'}
 | |
|         hasAttachments={hasAttachments}
 | |
|       >
 | |
|         {hasAttachments && (
 | |
|           <StyledImage>
 | |
|             {firstImageLikeAttachment ? (
 | |
|               <Image
 | |
|                 alt={getAlt(firstImageLikeAttachment)}
 | |
|                 attachment={firstImageLikeAttachment}
 | |
|                 height={100}
 | |
|                 width={100}
 | |
|                 url={getAbsoluteAttachmentPath((firstImageLikeAttachment as any).thumbnail.path)}
 | |
|                 softCorners={true}
 | |
|               />
 | |
|             ) : hasAudioAttachment ? (
 | |
|               <div style={{ margin: '0 var(--margins-xs) 0 0' }}>
 | |
|                 <SessionIcon iconType="microphone" iconSize="huge" />
 | |
|               </div>
 | |
|             ) : null}
 | |
|           </StyledImage>
 | |
|         )}
 | |
|         <StyledText
 | |
|           container={true}
 | |
|           flexDirection="column"
 | |
|           justifyContent={'center'}
 | |
|           alignItems={'flex-start'}
 | |
|         >
 | |
|           <p>{authorName}</p>
 | |
|           {subtitleText && <Subtle>{subtitleText}</Subtle>}
 | |
|         </StyledText>
 | |
|       </QuotedMessageCompositionReply>
 | |
|       <SessionIconButton
 | |
|         iconType="exit"
 | |
|         iconColor="var(--chat-buttons-icon-color)"
 | |
|         iconSize="small"
 | |
|         onClick={removeQuotedMessage}
 | |
|         margin={'0 var(--margins-sm) 0 0'}
 | |
|         aria-label={window.i18n('close')}
 | |
|       />
 | |
|     </QuotedMessageComposition>
 | |
|   );
 | |
| };
 |