From 0a6a49eda59a8eb3711545eed812744eb1d62a2a Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 3 Dec 2020 16:23:03 +1100 Subject: [PATCH] Add an Outgoing Message Status react component --- images/double-check.svg | 2 +- images/pow.svg | 6 - images/read.svg | 2 +- images/sending.svg | 12 -- js/background.js | 1 - stylesheets/_modules.scss | 197 ------------------ stylesheets/_theme_dark.scss | 20 -- ts/components/conversation/Message.tsx | 43 +--- .../message/OutgoingMessageStatus.tsx | 126 +++++++++++ ts/components/session/icon/Icons.tsx | 21 +- ts/components/session/icon/SessionIcon.tsx | 35 +++- 11 files changed, 190 insertions(+), 275 deletions(-) delete mode 100644 images/pow.svg delete mode 100644 images/sending.svg create mode 100644 ts/components/conversation/message/OutgoingMessageStatus.tsx diff --git a/images/double-check.svg b/images/double-check.svg index 8a6958f98..16765372d 100644 --- a/images/double-check.svg +++ b/images/double-check.svg @@ -4,7 +4,7 @@ double check Created with Sketch. - + diff --git a/images/pow.svg b/images/pow.svg deleted file mode 100644 index 830b9ada9..000000000 --- a/images/pow.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/images/read.svg b/images/read.svg index cedd36d0c..6ca2938ec 100644 --- a/images/read.svg +++ b/images/read.svg @@ -4,7 +4,7 @@ Icons/Read/read-18x12 Created with Sketch. - + diff --git a/images/sending.svg b/images/sending.svg deleted file mode 100644 index 68de68f85..000000000 --- a/images/sending.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - sending - Created with Sketch. - - - - - - - \ No newline at end of file diff --git a/js/background.js b/js/background.js index 5a178f4ea..db6d353df 100644 --- a/js/background.js +++ b/js/background.js @@ -92,7 +92,6 @@ 'reply.svg', 'save.svg', 'search.svg', - 'sending.svg', 'shield.svg', 'signal-laptop.png', 'signal-phone.png', diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index a3e627a43..6a6361e13 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -437,23 +437,6 @@ margin-inline-start: 5px; } -.module-message__metadata__status-icon--sending { - @include color-svg('../images/sending.svg', $color-gray-60); - animation: module-message__metdata__status-icon--spinning 4s linear infinite; -} - -.module-message__metadata__status-icon--pow { - @include color-svg('../images/pow.svg', $color-gray-60); - animation: module-message__metdata__status-icon--spinning 4s linear infinite; -} - -@keyframes module-message__metdata__status-icon--spinning { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - .module-message__metadata__status-icon--sent { @include color-svg('../images/check-circle-outline.svg', $color-gray-60); } @@ -1148,44 +1131,6 @@ font-weight: 300; } -.module-message-detail__contact__status-icon { - min-width: 12px; - min-height: 12px; - width: 12px; - height: 12px; - display: inline-block; - margin-bottom: 2px; - margin-inline-start: 5px; -} - -.module-message-detail__contact__status-icon--sending { - @include color-svg('../images/sending.svg', $color-gray-60); - animation: module-message-detail__contact__status-icon--spinning 4s linear - infinite; -} - -@keyframes module-message-detail__contact__status-icon--spinning { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - -.module-message-detail__contact__status-icon--sent { - @include color-svg('../images/check-circle-outline.svg', $color-gray-60); -} -.module-message-detail__contact__status-icon--delivered { - @include color-svg('../images/double-check.svg', $color-gray-60); - width: 18px; -} -.module-message-detail__contact__status-icon--read { - @include color-svg('../images/read.svg', $color-gray-60); - width: 18px; -} -.module-message-detail__contact__status-icon--error { - @include color-svg('../images/error.svg', $session-color-danger); -} - .module-message-detail__contact__unidentified-delivery-icon { margin-inline-start: 6px; margin-inline-end: 10px; @@ -1551,25 +1496,6 @@ margin-inline-start: 6px; } -.module-conversation-list-item__message__status-icon--sending { - @include color-svg('../images/sending.svg', $color-light-35); - animation: module-conversation-list-item__message__status-icon--spinning 4s - linear infinite; -} - -.module-conversation-list-item__message__status-icon--pow { - @include color-svg('../images/session/gear.svg', $color-light-35); - animation: module-conversation-list-item__message__status-icon--spinning 4s - linear infinite; -} - -@keyframes module-conversation-list-item__message__status-icon--spinning { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - .module-conversation-list-item__message__status-icon--sent { @include color-svg('../images/check-circle-outline.svg', $color-light-35); } @@ -1587,94 +1513,6 @@ // Module: Main Header -.module-main-header { - display: flex; - flex-direction: column; - border-bottom: 1px solid $color-dark-90; - color: $color-dark-05; -} - -.module-main-header__title { - height: 55px; - padding-inline-start: 16px; - flex: 1; - flex-direction: row; - display: flex; - align-items: center; - cursor: pointer; - - &:hover { - background-color: $color-dark-75; - } -} - -.module-main-header__menu { - color: $color-dark-05; - overflow: hidden; - - .accordian { - margin-top: -100%; - transition: margin-top 0.35s ease-out; - - &.expanded { - margin-top: 0; - } - } - - .menu-item { - padding: 12px; - background-color: $color-dark-90; - user-select: none; - cursor: pointer; - - &:hover { - background-color: $color-dark-75; - } - } -} - -.module-main-header-content-toggle { - width: 3em; - line-height: 3em; - font-weight: bold; - overflow: hidden; - user-select: none; - color: white; - text-align: center; - - &:after { - -webkit-transition: all 0.35s; - -o-transition: all 0.35s; - transition: all 0.35s; - width: 3em; - line-height: 3em; - height: 3em; - content: '\25BE'; - display: inline-block; - } -} - -.module-main-header-content-toggle-visible::after { - transform: rotate(180deg); -} - -.module-main-header__contact-name { - font-weight: 300; - margin-inline-start: 12px; - color: $color-dark-05; - overflow-x: auto; - flex: 1; -} - -.module-main-header__search { - margin: 8px; - position: relative; -} - -.module-main-header__search__icon { - background-color: $color-light-35; -} - .module-main-header__search__input { color: $color-dark-05; background-color: $color-gray-95; @@ -1702,41 +1540,6 @@ } } -.module-main-header__search__icon { - content: ''; - display: inline-block; - width: 18px; - height: 26px; - background-color: $color-light-35; - position: absolute; - left: 14px; - top: 3px; - - cursor: text; - @include color-svg('../images/search.svg', $color-gray-60); -} - -.module-main-header__search__cancel-icon { - position: absolute; - right: 16px; - top: 9px; - height: 14px; - width: 14px; - cursor: pointer; - @include color-svg('../images/x-16.svg', $color-gray-60); -} - -.module-main-header__search__copy-from-clipboard { - position: absolute; - right: 16px; - top: 5px; - width: 20px; - height: 22px; - opacity: 0.8; - cursor: pointer; - @include color-svg('../images/icon-paste.svg', $color-gray-60); -} - // Module: Image .module-image { diff --git a/stylesheets/_theme_dark.scss b/stylesheets/_theme_dark.scss index 8d96ac76f..941d7ff99 100644 --- a/stylesheets/_theme_dark.scss +++ b/stylesheets/_theme_dark.scss @@ -509,22 +509,6 @@ color: $color-gray-05; } - .module-conversation-list-item__message__status-icon--sending { - @include color-svg('../images/sending.svg', $color-light-35); - } - - .module-conversation-list-item__message__status-icon--sent { - @include color-svg('../images/check-circle-outline.svg', $color-light-35); - } - .module-conversation-list-item__message__status-icon--delivered { - @include color-svg('../images/double-check.svg', $color-light-35); - width: 18px; - } - .module-conversation-list-item__message__status-icon--read { - @include color-svg('../images/read.svg', $color-light-35); - width: 18px; - } - // Module: Main Header .module-main-header__search__input { @@ -543,10 +527,6 @@ } } - .module-main-header__search__cancel-icon { - @include color-svg('../images/x-16.svg', $color-gray-25); - } - // Module: Image .module-image { diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index 7eec17e71..f28f0017c 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -38,10 +38,10 @@ import _ from 'lodash'; import { animation, contextMenu, Item, Menu } from 'react-contexify'; import uuid from 'uuid'; import { InView } from 'react-intersection-observer'; -import { MetadataBadge, MetadataBadges } from './message/MetadataBadge'; -import { nonNullish } from '../../session/utils/String'; +import { MetadataBadges } from './message/MetadataBadge'; import { MetadataSpacer } from './message/MetadataUtilComponent'; import { DefaultTheme, withTheme } from 'styled-components'; +import { OutgoingMessageStatus } from './message/OutgoingMessageStatus'; // Same as MIN_WIDTH in ImageGrid.tsx const MINIMUM_LINK_PREVIEW_IMAGE_WIDTH = 200; @@ -226,7 +226,6 @@ class MessageInner extends React.PureComponent { ); } - // tslint:disable-next-line: cyclomatic-complexity public renderMetadata() { const { collapseMetadata, @@ -243,21 +242,13 @@ class MessageInner extends React.PureComponent { if (collapseMetadata) { return null; } + const isOutgoing = direction === 'outgoing'; const isShowingImage = this.isShowingImage(); const withImageNoCaption = Boolean(!text && isShowingImage); - const showError = status === 'error' && direction === 'outgoing'; - const showSentNoErrors = - !bodyPending && - direction === 'outgoing' && - status !== 'error' && - status !== 'sending' && - status !== 'pow'; - - const showSending = - !bodyPending && - direction === 'outgoing' && - (status === 'sending' || status === 'pow'); + const showError = status === 'error' && isOutgoing; + + const showStatus = Boolean(!bodyPending && status?.length && isOutgoing); return (
{ /> ) : null} - {bodyPending ? ( -
- -
- ) : null} + {bodyPending ? : null} - {showSending ? ( -
- ) : null} - {showSentNoErrors ? ( - ) : null}
diff --git a/ts/components/conversation/message/OutgoingMessageStatus.tsx b/ts/components/conversation/message/OutgoingMessageStatus.tsx new file mode 100644 index 000000000..411f6cd56 --- /dev/null +++ b/ts/components/conversation/message/OutgoingMessageStatus.tsx @@ -0,0 +1,126 @@ +import React from 'react'; +import styled, { DefaultTheme } from 'styled-components'; +import { + SessionIcon, + SessionIconSize, + SessionIconType, +} from '../../session/icon'; + +const MessageStatusSendingContainer = styled.div` + min-width: 12px; + min-height: 12px; + width: 12px; + height: 12px; + display: inline-block; + margin-bottom: 2px; + margin-inline-start: 5px; +`; + +const MessageStatusSending = (props: { + theme: DefaultTheme; + withImageNoCaption: boolean; +}) => { + const iconColor = props.withImageNoCaption ? 'white' : undefined; + return ( + + + + ); +}; + +const MessageStatusSent = (props: { + theme: DefaultTheme; + withImageNoCaption: boolean; +}) => { + const iconColor = props.withImageNoCaption ? 'white' : undefined; + + return ( + + + + ); +}; + +const MessageStatusDelivered = (props: { + theme: DefaultTheme; + withImageNoCaption: boolean; +}) => { + const iconColor = props.withImageNoCaption ? 'white' : undefined; + return ( + + + + ); +}; + +const MessageStatusRead = (props: { + theme: DefaultTheme; + withImageNoCaption: boolean; +}) => { + const iconColor = props.withImageNoCaption ? 'white' : undefined; + + return ( + + + + ); +}; + +const MessageStatusError = (props: { + theme: DefaultTheme; + withImageNoCaption: boolean; +}) => { + return ( + + + + ); +}; + +export const OutgoingMessageStatus = (props: { + status?: 'sending' | 'sent' | 'delivered' | 'read' | 'error' | 'pow'; + theme: DefaultTheme; + withImageNoCaption: boolean; +}) => { + switch (props.status) { + case 'pow': + case 'sending': + return ; + case 'sent': + return ; + case 'delivered': + return ; + case 'read': + return ; + case 'error': + return ; + default: + return null; + } +}; diff --git a/ts/components/session/icon/Icons.tsx b/ts/components/session/icon/Icons.tsx index 393b3c99e..3c6e21a83 100644 --- a/ts/components/session/icon/Icons.tsx +++ b/ts/components/session/icon/Icons.tsx @@ -36,9 +36,13 @@ export enum SessionIconType { Users = 'users', Upload = 'upload', Warning = 'warning', + Sending = 'sending', + DoubleCheck = 'doubleCheck', + Read = 'read', } export enum SessionIconSize { + Tiny = 'tiny', Small = 'small', Medium = 'medium', Large = 'large', @@ -79,7 +83,7 @@ export const icons = { [SessionIconType.CircleCheck]: { path: 'M4.77,7.61c-0.15-0.15-0.38-0.15-0.53,0c-0.15,0.15-0.15,0.38,0,0.53l1.88,1.88c0.15,0.15,0.38,0.15,0.53,0 l4.13-4.12c0.15-0.15,0.15-0.38,0-0.53c-0.15-0.15-0.38-0.15-0.53,0L6.38,9.22L4.77,7.61z', - viewBox: '0 0 15 15', + viewBox: '4 4 7 7', }, [SessionIconType.CircleCheckFilled]: { path: @@ -231,4 +235,19 @@ export const icons = { 'M243.225,333.382c-13.6,0-25,11.4-25,25s11.4,25,25,25c13.1,0,25-11.4,24.4-24.4 C268.225,344.682,256.925,333.382,243.225,333.382z M474.625,421.982c15.7-27.1,15.8-59.4,0.2-86.4l-156.6-271.2c-15.5-27.3-43.5-43.5-74.9-43.5s-59.4,16.3-74.9,43.4 l-156.8,271.5c-15.6,27.3-15.5,59.8,0.3,86.9c15.6,26.8,43.5,42.9,74.7,42.9h312.8 C430.725,465.582,458.825,449.282,474.625,421.982z M440.625,402.382c-8.7,15-24.1,23.9-41.3,23.9h-312.8 c-17,0-32.3-8.7-40.8-23.4c-8.6-14.9-8.7-32.7-0.1-47.7l156.8-271.4c8.5-14.9,23.7-23.7,40.9-23.7c17.1,0,32.4,8.9,40.9,23.8 l156.7,271.4C449.325,369.882,449.225,387.482,440.625,402.382z M237.025,157.882c-11.9,3.4-19.3,14.2-19.3,27.3c0.6,7.9,1.1,15.9,1.7,23.8c1.7,30.1,3.4,59.6,5.1,89.7 c0.6,10.2,8.5,17.6,18.7,17.6c10.2,0,18.2-7.9,18.7-18.2c0-6.2,0-11.9,0.6-18.2c1.1-19.3,2.3-38.6,3.4-57.9 c0.6-12.5,1.7-25,2.3-37.5c0-4.5-0.6-8.5-2.3-12.5C260.825,160.782,248.925,155.082,237.025,157.882z', viewBox: '0 0 486.463 486.463', }, + [SessionIconType.Sending]: { + path: + 'M11.5,0 L12.5,0 L12.5,1 L11.5,1 L11.5,0 Z M11.5,11 L12.5,11 L12.5,12 L11.5,12 L11.5,11 Z M6,5.5 L7,5.5 L7,6.5 L6,6.5 L6,5.5 Z M17,5.5 L18,5.5 L18,6.5 L17,6.5 L17,5.5 Z M16.9461524,2.5669873 L17.4461524,3.4330127 L16.580127,3.9330127 L16.080127,3.0669873 L16.9461524,2.5669873 Z M7.41987298,8.0669873 L7.91987298,8.9330127 L7.05384758,9.4330127 L6.55384758,8.5669873 L7.41987298,8.0669873 Z M9.4330127,0.553847577 L9.9330127,1.41987298 L9.0669873,1.91987298 L8.5669873,1.05384758 L9.4330127,0.553847577 Z M14.9330127,10.080127 L15.4330127,10.9461524 L14.5669873,11.4461524 L14.0669873,10.580127 L14.9330127,10.080127 Z M14.5669873,0.553847577 L15.4330127,1.05384758 L14.9330127,1.91987298 L14.0669873,1.41987298 L14.5669873,0.553847577 Z M9.0669873,10.080127 L9.9330127,10.580127 L9.4330127,11.4461524 L8.5669873,10.9461524 L9.0669873,10.080127 Z M7.05384758,2.5669873 L7.91987298,3.0669873 L7.41987298,3.9330127 L6.55384758,3.4330127 L7.05384758,2.5669873 Z M16.580127,8.0669873 L17.4461524,8.5669873 L16.9461524,9.4330127 L16.080127,8.9330127 L16.580127,8.0669873 Z', + viewBox: '6 0 12 12', + }, + [SessionIconType.DoubleCheck]: { + path: + 'M7.91731278,0.313257194 C7.58941091,0.549084144 7.28273546,0.812570593 7.00070199,1.10030099 C6.67734551,1.03453102 6.34268082,1 6,1 C3.24,1 1,3.24 1,6 C1,8.76 3.24,11 6,11 C6.34268082,11 6.67734551,10.965469 7.00070199,10.899699 C7.28273546,11.1874294 7.58941091,11.4509159 7.91731278,11.6867428 C7.31518343,11.8898758 6.67037399,12 6,12 C2.688,12 0,9.312 0,6 C0,2.688 2.688,0 6,0 C6.67037399,0 7.31518343,0.110124239 7.91731278,0.313257194 Z M5.07266453,7.01233547 C5.12977459,7.4065842 5.21974274,7.79019382 5.33970233,8.16029767 L5,8.5 L2.5,6 L3.205,5.295 L5,7.085 L5.07266453,7.01233547 Z M12,0 C15.312,0 18,2.688 18,6 C18,9.312 15.312,12 12,12 C8.688,12 6,9.312 6,6 C6,2.688 8.688,0 12,0 Z M12,1 C9.24,1 7,3.24 7,6 C7,8.76 9.24,11 12,11 C14.76,11 17,8.76 17,6 C17,3.24 14.76,1 12,1 Z M11,8.5 L8.5,6 L9.205,5.295 L11,7.085 L14.795,3.29 L15.5,4 L11,8.5 Z', + viewBox: '6 0 12 12', + }, + [SessionIconType.Read]: { + path: + 'M7.91731278,0.313257194 C6.15053376,1.58392424 5,3.65760134 5,6 C5,6.343797 5.0247846,6.68180525 5.07266453,7.01233547 L5,7.085 L3.205,5.295 L2.5,6 L5,8.5 L5.33970233,8.16029767 C5.80439817,9.59399486 6.71914823,10.8250231 7.91731278,11.6867428 C7.31518343,11.8898758 6.67037399,12 6,12 C2.688,12 0,9.312 0,6 C0,2.688 2.688,0 6,0 C6.67037399,0 7.31518343,0.110124239 7.91731278,0.313257194 Z M12,0 C15.312,0 18,2.688 18,6 C18,9.312 15.312,12 12,12 C8.688,12 6,9.312 6,6 C6,2.688 8.688,0 12,0 Z M11,8.5 L15.5,4 L14.795,3.29 L11,7.085 L9.205,5.295 L8.5,6 L11,8.5 Z', + viewBox: '6 0 12 12', + }, }; diff --git a/ts/components/session/icon/SessionIcon.tsx b/ts/components/session/icon/SessionIcon.tsx index 307a5c10f..35707c45b 100644 --- a/ts/components/session/icon/SessionIcon.tsx +++ b/ts/components/session/icon/SessionIcon.tsx @@ -1,13 +1,14 @@ import React from 'react'; import { icons, SessionIconSize, SessionIconType } from '../icon'; -import styled, { DefaultTheme } from 'styled-components'; +import styled, { css, DefaultTheme, keyframes } from 'styled-components'; export type SessionIconProps = { iconType: SessionIconType; iconSize: SessionIconSize | number; iconColor?: string; iconRotation?: number; + rotateDuration?: number; theme: DefaultTheme; }; @@ -16,6 +17,8 @@ const getIconDimensionFromIconSize = (iconSize: SessionIconSize | number) => { return iconSize; } else { switch (iconSize) { + case SessionIconSize.Tiny: + return '12'; case SessionIconSize.Small: return '15'; case SessionIconSize.Medium: @@ -36,13 +39,36 @@ type StyledSvgProps = { width: string | number; height: string | number; iconRotation: number; + rotateDuration?: number; }; +const rotate = keyframes` + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +`; + +const animation = (props: any) => { + if (props.rotateDuration) { + return css` + ${rotate} ${props.rotateDuration}s infinite linear; + `; + } else { + return; + } +}; + +//tslint:disable no-unnecessary-callback-wrapper const Svg = styled.svg` width: ${props => props.width}; height: ${props => props.height}; + animation: ${props => animation(props)}; transform: ${props => `rotate(${props.iconRotation}deg)`}; `; +//tslint:enable no-unnecessary-callback-wrapper const SessionSvg = (props: { viewBox: string; @@ -51,9 +77,11 @@ const SessionSvg = (props: { height: string | number; iconRotation: number; iconColor?: string; + rotateDuration?: number; theme: DefaultTheme; }) => { const colorSvg = props.iconColor || props?.theme?.colors.textColor || 'red'; + return ( @@ -62,15 +90,13 @@ const SessionSvg = (props: { }; export const SessionIcon = (props: SessionIconProps) => { - const { iconType, iconColor, theme } = props; + const { iconType, iconColor, theme, rotateDuration } = props; let { iconSize, iconRotation } = props; iconSize = iconSize || SessionIconSize.Medium; - // default to whatever the text color is near this svg iconRotation = iconRotation || 0; const iconDimensions = getIconDimensionFromIconSize(iconSize); const iconDef = icons[iconType]; - // const themeContext = useContext(ThemeContext); if (!theme) { window.log.error('Missing theme props in SessionIcon'); @@ -82,6 +108,7 @@ export const SessionIcon = (props: SessionIconProps) => { path={iconDef.path} width={iconDimensions} height={iconDimensions} + rotateDuration={rotateDuration} iconRotation={iconRotation} iconColor={iconColor} theme={theme}