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);
+}