diff --git a/stylesheets/_emoji.scss b/stylesheets/_emoji.scss index 048380944..0ebd4a9c4 100644 --- a/stylesheets/_emoji.scss +++ b/stylesheets/_emoji.scss @@ -44,27 +44,33 @@ span.emoji-inner { } img.emoji { - width: 1.4em; height: 1.4em; margin-bottom: -5px; + margin-left: 2px; + margin-right: 2px; vertical-align: baseline; + width: 1.4em; } img.emoji.small { - width: 1.8em; - height: 1.8em; + width: 32px; + height: 32px; } img.emoji.medium { - width: 2em; - height: 2em; + width: 36px; + height: 36px; } img.emoji.large { - width: 2.5em; - height: 2.5em; + width: 40px; + height: 40px; } -img.emoji.jumbo { - width: 3.5em; - height: 3.5em; +img.emoji.extra-large { + width: 48px; + height: 48px; +} +img.emoji.max { + width: 56px; + height: 56px; } // we need these, or we'll make conversation items too big in the left-nav diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index 6089fa9a6..c870a95ab 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -418,6 +418,10 @@ padding-bottom: 3px; } +.module-message__container--emoji { + padding-top: 0; +} + .module-message__container--outgoing { @include light-theme { background-color: $color-ultramarine; diff --git a/ts/components/conversation/Emojify.stories.tsx b/ts/components/conversation/Emojify.stories.tsx index edb62cea6..2d0c304b5 100644 --- a/ts/components/conversation/Emojify.stories.tsx +++ b/ts/components/conversation/Emojify.stories.tsx @@ -35,7 +35,16 @@ story.add('Skin Color Modifier', () => { story.add('Jumbo', () => { const props = createProps({ text: '😹😹😹', - sizeClass: 'jumbo', + sizeClass: 'max', + }); + + return ; +}); + +story.add('Extra Large', () => { + const props = createProps({ + text: '😹😹😹', + sizeClass: 'extra-large', }); return ; diff --git a/ts/components/conversation/Message.stories.tsx b/ts/components/conversation/Message.stories.tsx index 8beed4e7d..239243496 100644 --- a/ts/components/conversation/Message.stories.tsx +++ b/ts/components/conversation/Message.stories.tsx @@ -192,6 +192,22 @@ story.add('Plain Message', () => { return renderBothDirections(props); }); +story.add('Emoji Messages', () => ( + <> + +
+ +
+ +
+ +
+ +
+ + +)); + story.add('Delivered', () => { const props = createProps({ direction: 'outgoing', diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index a29533963..f0dbef5a6 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -63,7 +63,8 @@ import { CustomColorType, } from '../../types/Colors'; import { createRefMerger } from '../../util/refMerger'; -import { emojiToData } from '../emoji/lib'; +import { emojiToData, getEmojiCount } from '../emoji/lib'; +import { isEmojiOnlyText } from '../../util/isEmojiOnlyText'; import type { SmartReactionPicker } from '../../state/smart/ReactionPicker'; import { getCustomColorStyle } from '../../util/getCustomColorStyle'; import { offsetDistanceModifier } from '../../util/popperUtil'; @@ -618,6 +619,11 @@ export class Message extends React.PureComponent { return null; } + const isEmojiOnly = Boolean( + text && isEmojiOnlyText(text) && getEmojiCount(text) < 6 + ); + const isStickerLike = isSticker || isEmojiOnly; + return ( { i18n={i18n} id={id} isShowingImage={this.isShowingImage()} - isSticker={isSticker} + isSticker={isStickerLike} isTapToViewExpired={isTapToViewExpired} showMessageDetail={showMessageDetail} status={status} @@ -2325,6 +2331,7 @@ export class Message extends React.PureComponent { isTapToView, isTapToViewExpired, isTapToViewError, + text, } = this.props; const { isSelected } = this.state; @@ -2333,17 +2340,24 @@ export class Message extends React.PureComponent { const width = this.getWidth(); const isShowingImage = this.isShowingImage(); + const isEmojiOnly = + text && isEmojiOnlyText(text) && getEmojiCount(text) < 6; + const isStickerLike = isSticker || isEmojiOnly; + const containerClassnames = classNames( 'module-message__container', isGIF(attachments) ? 'module-message__container--gif' : null, - isSelected && !isSticker ? 'module-message__container--selected' : null, - isSticker ? 'module-message__container--with-sticker' : null, - !isSticker ? `module-message__container--${direction}` : null, + isSelected && !isStickerLike + ? 'module-message__container--selected' + : null, + isStickerLike ? 'module-message__container--with-sticker' : null, + !isStickerLike ? `module-message__container--${direction}` : null, + isEmojiOnly ? 'module-message__container--emoji' : null, isTapToView ? 'module-message__container--with-tap-to-view' : null, isTapToView && isTapToViewExpired ? 'module-message__container--with-tap-to-view-expired' : null, - !isSticker && direction === 'outgoing' + !isStickerLike && direction === 'outgoing' ? `module-message__container--outgoing-${conversationColor}` : null, isTapToView && isAttachmentPending && !isTapToViewExpired @@ -2363,7 +2377,7 @@ export class Message extends React.PureComponent { const containerStyles = { width: isShowingImage ? width : undefined, }; - if (!isSticker && direction === 'outgoing') { + if (!isStickerLike && direction === 'outgoing') { Object.assign(containerStyles, getCustomColorStyle(customColor)); } diff --git a/ts/components/emoji/lib.ts b/ts/components/emoji/lib.ts index 81ddb9877..731747f73 100644 --- a/ts/components/emoji/lib.ts +++ b/ts/components/emoji/lib.ts @@ -26,7 +26,13 @@ import * as log from '../../logging/log'; export const skinTones = ['1F3FB', '1F3FC', '1F3FD', '1F3FE', '1F3FF']; export type SkinToneKey = '1F3FB' | '1F3FC' | '1F3FD' | '1F3FE' | '1F3FF'; -export type SizeClassType = '' | 'small' | 'medium' | 'large' | 'jumbo'; +export type SizeClassType = + | '' + | 'small' + | 'medium' + | 'large' + | 'extra-large' + | 'max'; export type EmojiSkinVariation = { unified: string; @@ -315,19 +321,22 @@ export function getSizeClass(str: string): SizeClassType { const emojiCount = getEmojiCount(str); - if (emojiCount > 8) { - return ''; + if (emojiCount === 1) { + return 'max'; } - if (emojiCount > 6) { - return 'small'; + if (emojiCount === 2) { + return 'extra-large'; } - if (emojiCount > 4) { - return 'medium'; - } - if (emojiCount > 2) { + if (emojiCount === 3) { return 'large'; } - return 'jumbo'; + if (emojiCount === 4) { + return 'medium'; + } + if (emojiCount === 5) { + return 'small'; + } + return ''; } data.forEach(emoji => { diff --git a/ts/util/avatarTextSizeCalculator.ts b/ts/util/avatarTextSizeCalculator.ts index 095b7d16c..aaae81488 100644 --- a/ts/util/avatarTextSizeCalculator.ts +++ b/ts/util/avatarTextSizeCalculator.ts @@ -1,8 +1,8 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import * as grapheme from './grapheme'; import { getEmojiCount } from '../components/emoji/lib'; +import { isEmojiOnlyText } from './isEmojiOnlyText'; type FontSizes = { diameter: number; @@ -33,7 +33,7 @@ export function getFittedFontSize( const sizes = getFontSizes(bubbleSize); let candidateFontSize = sizes.text; - if (grapheme.count(text) === 1 && getEmojiCount(text) === 1) { + if (isEmojiOnlyText(text) && getEmojiCount(text) === 1) { candidateFontSize = sizes.singleEmoji; } diff --git a/ts/util/isEmojiOnlyText.tsx b/ts/util/isEmojiOnlyText.tsx new file mode 100644 index 000000000..958227e5a --- /dev/null +++ b/ts/util/isEmojiOnlyText.tsx @@ -0,0 +1,9 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import * as grapheme from './grapheme'; +import { getEmojiCount } from '../components/emoji/lib'; + +export function isEmojiOnlyText(text: string): boolean { + return grapheme.count(text) === getEmojiCount(text); +}