import React, { useState } from 'react'; import classNames from 'classnames'; import * as MIME from '../../../ts/types/MIME'; import * as GoogleChrome from '../../../ts/util/GoogleChrome'; import { MessageBody } from './MessageBody'; import { ContactName } from './ContactName'; import { PubKey } from '../../session/types'; import { useEncryptedFileFetch } from '../../hooks/useEncryptedFileFetch'; import { useSelector } from 'react-redux'; import { getSelectedConversationKey, isGroupConversation, isPublicGroupConversation, } from '../../state/selectors/conversations'; import { noop } from 'underscore'; import { useDisableDrag } from '../../hooks/useDisableDrag'; export type QuotePropsWithoutListener = { attachment?: QuotedAttachmentType; authorPhoneNumber: string; authorProfileName?: string; authorName?: string; isFromMe: boolean; isIncoming: boolean; text: string | null; referencedMessageNotFound: boolean; }; export type QuotePropsWithListener = QuotePropsWithoutListener & { onClick?: (e: React.MouseEvent) => void; }; export interface QuotedAttachmentType { contentType: MIME.MIMEType; fileName: string; /** Not included in protobuf */ isVoiceMessage: boolean; thumbnail?: Attachment; } interface Attachment { contentType: MIME.MIMEType; /** Not included in protobuf, and is loaded asynchronously */ objectUrl?: string; } function validateQuote(quote: QuotePropsWithoutListener): boolean { if (quote.text) { return true; } if (quote.attachment) { return true; } return false; } function getObjectUrl(thumbnail: Attachment | undefined): string | undefined { if (thumbnail && thumbnail.objectUrl) { return thumbnail.objectUrl; } return; } function getTypeLabel({ contentType, isVoiceMessage, }: { contentType: MIME.MIMEType; isVoiceMessage: boolean; }): string | undefined { if (GoogleChrome.isVideoTypeSupported(contentType)) { return window.i18n('video'); } if (GoogleChrome.isImageTypeSupported(contentType)) { return window.i18n('photo'); } if (MIME.isAudio(contentType) && isVoiceMessage) { return window.i18n('voiceMessage'); } if (MIME.isAudio(contentType)) { return window.i18n('audio'); } return; } export const QuoteIcon = (props: any) => { const { icon } = props; return (
); }; export const QuoteImage = (props: { handleImageErrorBound: () => void; url: string; contentType: string; icon?: string; }) => { const { url, icon, contentType, handleImageErrorBound } = props; const disableDrag = useDisableDrag(); const { loading, urlToLoad } = useEncryptedFileFetch(url, contentType, false); const srcData = !loading ? urlToLoad : ''; const iconElement = icon ? (
) : null; return (
{window.i18n('quoteThumbnailAlt')} {iconElement}
); }; export const QuoteGenericFile = ( props: Pick ) => { const { attachment, isIncoming } = props; if (!attachment) { return null; } const { fileName, contentType } = attachment; const isGenericFile = !GoogleChrome.isVideoTypeSupported(contentType) && !GoogleChrome.isImageTypeSupported(contentType) && !MIME.isAudio(contentType); if (!isGenericFile) { return null; } return (
{fileName}
); }; export const QuoteIconContainer = ( props: Pick & { handleImageErrorBound: () => void; imageBroken: boolean; } ) => { const { attachment, imageBroken, handleImageErrorBound } = props; if (!attachment) { return null; } const { contentType, thumbnail } = attachment; const objectUrl = getObjectUrl(thumbnail); if (GoogleChrome.isVideoTypeSupported(contentType)) { return objectUrl && !imageBroken ? ( ) : ( ); } if (GoogleChrome.isImageTypeSupported(contentType)) { return objectUrl && !imageBroken ? ( ) : ( ); } if (MIME.isAudio(contentType)) { return ; } return null; }; export const QuoteText = ( props: Pick ) => { const { text, attachment, isIncoming } = props; const isGroup = useSelector(isGroupConversation); const convoId = useSelector(getSelectedConversationKey); if (text) { return (
); } if (!attachment) { return null; } const { contentType, isVoiceMessage } = attachment; const typeLabel = getTypeLabel({ contentType, isVoiceMessage }); if (typeLabel) { return (
{typeLabel}
); } return null; }; type QuoteAuthorProps = { authorPhoneNumber: string; authorProfileName?: string; authorName?: string; isFromMe: boolean; isIncoming: boolean; showPubkeyForAuthor?: boolean; }; const QuoteAuthor = (props: QuoteAuthorProps) => { const { authorProfileName, authorPhoneNumber, authorName, isFromMe, isIncoming } = props; return (
{isFromMe ? ( window.i18n('you') ) : ( )}
); }; export const QuoteReferenceWarning = ( props: Pick ) => { const { isIncoming, referencedMessageNotFound } = props; if (!referencedMessageNotFound) { return null; } return (
{window.i18n('originalMessageNotFound')}
); }; export const Quote = (props: QuotePropsWithListener) => { const [imageBroken, setImageBroken] = useState(false); const handleImageErrorBound = () => { setImageBroken(true); }; const isPublic = useSelector(isPublicGroupConversation); if (!validateQuote(props)) { return null; } const { isIncoming, referencedMessageNotFound, attachment, text, onClick } = props; return (
); };