diff --git a/_locales/en/messages.json b/_locales/en/messages.json index c0effc14e..0df30a7e1 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -45,6 +45,7 @@ "incomingError": "Error handling incoming message", "media": "Media", "mediaEmptyState": "No media", + "document": "Document", "documents": "Documents", "documentsEmptyState": "No documents", "today": "Today", diff --git a/ts/components/conversation/message/message-content/quote/Quote.tsx b/ts/components/conversation/message/message-content/quote/Quote.tsx index f7453b2bb..fd3d9ce6f 100644 --- a/ts/components/conversation/message/message-content/quote/Quote.tsx +++ b/ts/components/conversation/message/message-content/quote/Quote.tsx @@ -1,5 +1,4 @@ import React, { useState, MouseEvent } from 'react'; -import classNames from 'classnames'; import * as MIME from '../../../../../types/MIME'; @@ -7,7 +6,6 @@ import { useSelector } from 'react-redux'; import { isPublicGroupConversation } from '../../../../../state/selectors/conversations'; import { QuoteAuthor } from './QuoteAuthor'; -import { QuoteGenericFile } from './QuoteGenericFile'; import { QuoteText } from './QuoteText'; import { QuoteIconContainer } from './QuoteIconContainer'; import styled from 'styled-components'; @@ -54,6 +52,11 @@ function validateQuote(quote: QuotePropsWithoutListener): boolean { return false; } +const StyledQuoteContainer = styled.div` + min-width: 300px; // if the quoted content is small it doesn't look very good so we set a minimum + padding-right: var(--margins-xs); +`; + const StyledQuote = styled.div<{ hasAttachment: boolean; isIncoming: boolean; @@ -64,7 +67,7 @@ const StyledQuote = styled.div<{ display: flex; flex-direction: row; align-items: stretch; - overflow: hidden; + margin: ${props => (props.hasAttachment ? 'var(--margins-md)' : 'var(--margins-xs)')} 0; ${props => !props.hasAttachment && 'border-left: 4px solid;'} border-color: ${props => props.isIncoming @@ -78,6 +81,10 @@ const StyledQuoteTextContent = styled.div` padding-inline-start: 10px; padding-inline-end: 10px; max-width: 100%; + + display: flex; + flex-direction: column; + justify-content: center; `; export const Quote = (props: QuotePropsWithListener) => { @@ -95,7 +102,7 @@ export const Quote = (props: QuotePropsWithListener) => { const { isIncoming, attachment, text, onClick } = props; return ( -
+ { isIncoming={props.isIncoming} showPubkeyForAuthor={isPublic} /> - -
+ ); }; diff --git a/ts/components/conversation/message/message-content/quote/QuoteIconContainer.tsx b/ts/components/conversation/message/message-content/quote/QuoteIconContainer.tsx index 330dde711..db08b8203 100644 --- a/ts/components/conversation/message/message-content/quote/QuoteIconContainer.tsx +++ b/ts/components/conversation/message/message-content/quote/QuoteIconContainer.tsx @@ -3,9 +3,10 @@ import { Attachment, QuotePropsWithoutListener } from './Quote'; import { GoogleChrome } from '../../../../../util'; import { MIME } from '../../../../../types'; -import { noop } from 'lodash'; +import { isEmpty, noop } from 'lodash'; import { QuoteImage } from './QuoteImage'; -import classNames from 'classnames'; +import styled from 'styled-components'; +import { SessionIconType, icons } from '../../../../icon'; function getObjectUrl(thumbnail: Attachment | undefined): string | undefined { if (thumbnail && thumbnail.objectUrl) { @@ -15,22 +16,66 @@ function getObjectUrl(thumbnail: Attachment | undefined): string | undefined { return; } -const QuoteIcon = (props: any) => { +const StyledQuoteIconContainer = styled.div` + flex: initial; + min-width: 54px; + width: 54px; + max-height: 54px; + position: relative; +`; + +const StyledQuoteIcon = styled.div` + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + + text-align: center; + display: flex; + align-items: center; + justify-content: center; +`; + +const StyledQuoteIconBackground = styled.div` + display: flex; + align-items: center; + justify-content: center; + + height: 54px; + width: 54px; + border-radius: var(--margins-sm); + background-color: var(--message-link-preview-background-color); + + &:hover { + background-color: var(--message-link-preview-background-color); + } + + svg { + width: 29px; + height: 29px; + fill: currentColor; + } +`; + +type QuoteIconProps = { + icon: Extract; +}; + +const QuoteIcon = (props: QuoteIconProps) => { const { icon } = props; + const iconProps = icons[icon]; return ( -
-
-
-
-
-
-
+ + + + + + + + + ); }; @@ -42,11 +87,20 @@ export const QuoteIconContainer = ( ) => { const { attachment, imageBroken, handleImageErrorBound } = props; - if (!attachment) { + if (!attachment || isEmpty(attachment)) { return null; } const { contentType, thumbnail } = attachment; + const isGenericFile = + !GoogleChrome.isVideoTypeSupported(contentType) && + !GoogleChrome.isImageTypeSupported(contentType) && + !MIME.isAudio(contentType); + + if (isGenericFile) { + return ; + } + const objectUrl = getObjectUrl(thumbnail); if (GoogleChrome.isVideoTypeSupported(contentType)) { @@ -61,6 +115,7 @@ export const QuoteIconContainer = ( ); } + if (GoogleChrome.isImageTypeSupported(contentType)) { return objectUrl && !imageBroken ? ( ); } + if (MIME.isAudio(contentType)) { return ; } + return null; }; diff --git a/ts/components/icon/Icons.tsx b/ts/components/icon/Icons.tsx index 5eebb10db..9f1f2c6b2 100644 --- a/ts/components/icon/Icons.tsx +++ b/ts/components/icon/Icons.tsx @@ -30,12 +30,14 @@ export type SessionIconType = | 'gear' | 'group' | 'hangup' + | 'image' | 'info' | 'link' | 'messageRequest' | 'microphone' | 'microphoneFull' | 'moon' + | 'movie' | 'mute' | 'oxen' | 'externalLink' @@ -268,6 +270,12 @@ export const icons = { viewBox: '0 0 1000 1000', ratio: 1, }, + image: { + path: + 'M8.5,13.5L11,16.5L14.5,12L19,18H5M21,19V5C21,3.89 20.1,3 19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19Z', + viewBox: '0 0 24 24', + ratio: 1, + }, info: { path: 'M17.5,2.4c-1.82-1.5-4.21-2.1-6.57-1.64c-3.09,0.6-5.57,3.09-6.15,6.19c-0.4,2.1,0.04,4.21,1.22,5.95 C7.23,14.7,8,16.41,8.36,18.12c0.17,0.81,0.89,1.41,1.72,1.41h4.85c0.83,0,1.55-0.59,1.72-1.42c0.37-1.82,1.13-3.55,2.19-4.99 c1-1.36,1.53-2.96,1.53-4.65C20.37,6.11,19.32,3.9,17.5,2.4z M17.47,12.11c-1.21,1.64-2.07,3.6-2.55,5.72l-4.91-0.05 c-0.4-1.93-1.25-3.84-2.62-5.84c-0.93-1.36-1.27-3.02-0.95-4.67c0.46-2.42,2.39-4.37,4.81-4.83c0.41-0.08,0.82-0.12,1.23-0.12 c1.44,0,2.82,0.49,3.94,1.4c1.43,1.18,2.25,2.91,2.25,4.76C18.67,9.79,18.25,11.04,17.47,12.11z M15.94,20.27H9.61c-0.47,0-0.85,0.38-0.85,0.85s0.38,0.85,0.85,0.85h6.33c0.47,0,0.85-0.38,0.85-0.85 S16.41,20.27,15.94,20.27z M15.94,22.7H9.61c-0.47,0-0.85,0.38-0.85,0.85s0.38,0.85,0.85,0.85h6.33c0.47,0,0.85-0.38,0.85-0.85 S16.41,22.7,15.94,22.7z M12.5,3.28c-2.89,0-5.23,2.35-5.23,5.23c0,0.47,0.38,0.85,0.85,0.85s0.85-0.38,0.85-0.85 c0-1.95,1.59-3.53,3.54-3.53c0.47,0,0.85-0.38,0.85-0.85S12.97,3.28,12.5,3.28z', @@ -304,6 +312,12 @@ export const icons = { viewBox: '0 0 20 21', ratio: 1, }, + movie: { + path: + 'M18,4L20,8H17L15,4H13L15,8H12L10,4H8L10,8H7L5,4H4A2,2 0 0,0 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V4H18Z', + viewBox: '0 0 24 24', + ratio: 1, + }, mute: { path: 'M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM12,6.5c2.49,0 4,2.02 4,4.5v0.1l2,2L18,11c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68c-0.24,0.06 -0.47,0.15 -0.69,0.23l1.64,1.64c0.18,-0.02 0.36,-0.05 0.55,-0.05zM5.41,3.35L4,4.76l2.81,2.81C6.29,8.57 6,9.74 6,11v5l-2,2v1h14.24l1.74,1.74 1.41,-1.41L5.41,3.35zM16,17L8,17v-6c0,-0.68 0.12,-1.32 0.34,-1.9L16,16.76L16,17z', diff --git a/ts/types/LocalizerKeys.ts b/ts/types/LocalizerKeys.ts index 5c6900f7b..76fcb30bb 100644 --- a/ts/types/LocalizerKeys.ts +++ b/ts/types/LocalizerKeys.ts @@ -45,6 +45,7 @@ export type LocalizerKeys = | 'incomingError' | 'media' | 'mediaEmptyState' + | 'document' | 'documents' | 'documentsEmptyState' | 'today'