Render emoji like a sticker
This commit is contained in:
parent
1466f9f1b2
commit
7c9b8e919c
|
@ -44,27 +44,33 @@ span.emoji-inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
img.emoji {
|
img.emoji {
|
||||||
width: 1.4em;
|
|
||||||
height: 1.4em;
|
height: 1.4em;
|
||||||
margin-bottom: -5px;
|
margin-bottom: -5px;
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: 2px;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
|
width: 1.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.emoji.small {
|
img.emoji.small {
|
||||||
width: 1.8em;
|
width: 32px;
|
||||||
height: 1.8em;
|
height: 32px;
|
||||||
}
|
}
|
||||||
img.emoji.medium {
|
img.emoji.medium {
|
||||||
width: 2em;
|
width: 36px;
|
||||||
height: 2em;
|
height: 36px;
|
||||||
}
|
}
|
||||||
img.emoji.large {
|
img.emoji.large {
|
||||||
width: 2.5em;
|
width: 40px;
|
||||||
height: 2.5em;
|
height: 40px;
|
||||||
}
|
}
|
||||||
img.emoji.jumbo {
|
img.emoji.extra-large {
|
||||||
width: 3.5em;
|
width: 48px;
|
||||||
height: 3.5em;
|
height: 48px;
|
||||||
|
}
|
||||||
|
img.emoji.max {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need these, or we'll make conversation items too big in the left-nav
|
// we need these, or we'll make conversation items too big in the left-nav
|
||||||
|
|
|
@ -418,6 +418,10 @@
|
||||||
padding-bottom: 3px;
|
padding-bottom: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.module-message__container--emoji {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.module-message__container--outgoing {
|
.module-message__container--outgoing {
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
background-color: $color-ultramarine;
|
background-color: $color-ultramarine;
|
||||||
|
|
|
@ -35,7 +35,16 @@ story.add('Skin Color Modifier', () => {
|
||||||
story.add('Jumbo', () => {
|
story.add('Jumbo', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
text: '😹😹😹',
|
text: '😹😹😹',
|
||||||
sizeClass: 'jumbo',
|
sizeClass: 'max',
|
||||||
|
});
|
||||||
|
|
||||||
|
return <Emojify {...props} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('Extra Large', () => {
|
||||||
|
const props = createProps({
|
||||||
|
text: '😹😹😹',
|
||||||
|
sizeClass: 'extra-large',
|
||||||
});
|
});
|
||||||
|
|
||||||
return <Emojify {...props} />;
|
return <Emojify {...props} />;
|
||||||
|
|
|
@ -192,6 +192,22 @@ story.add('Plain Message', () => {
|
||||||
return renderBothDirections(props);
|
return renderBothDirections(props);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
story.add('Emoji Messages', () => (
|
||||||
|
<>
|
||||||
|
<Message {...createProps({ text: '😀' })} />
|
||||||
|
<br />
|
||||||
|
<Message {...createProps({ text: '😀😀' })} />
|
||||||
|
<br />
|
||||||
|
<Message {...createProps({ text: '😀😀😀' })} />
|
||||||
|
<br />
|
||||||
|
<Message {...createProps({ text: '😀😀😀😀' })} />
|
||||||
|
<br />
|
||||||
|
<Message {...createProps({ text: '😀😀😀😀😀' })} />
|
||||||
|
<br />
|
||||||
|
<Message {...createProps({ text: '😀😀😀😀😀😀😀' })} />
|
||||||
|
</>
|
||||||
|
));
|
||||||
|
|
||||||
story.add('Delivered', () => {
|
story.add('Delivered', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
direction: 'outgoing',
|
direction: 'outgoing',
|
||||||
|
|
|
@ -63,7 +63,8 @@ import {
|
||||||
CustomColorType,
|
CustomColorType,
|
||||||
} from '../../types/Colors';
|
} from '../../types/Colors';
|
||||||
import { createRefMerger } from '../../util/refMerger';
|
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 type { SmartReactionPicker } from '../../state/smart/ReactionPicker';
|
||||||
import { getCustomColorStyle } from '../../util/getCustomColorStyle';
|
import { getCustomColorStyle } from '../../util/getCustomColorStyle';
|
||||||
import { offsetDistanceModifier } from '../../util/popperUtil';
|
import { offsetDistanceModifier } from '../../util/popperUtil';
|
||||||
|
@ -618,6 +619,11 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isEmojiOnly = Boolean(
|
||||||
|
text && isEmojiOnlyText(text) && getEmojiCount(text) < 6
|
||||||
|
);
|
||||||
|
const isStickerLike = isSticker || isEmojiOnly;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageMetadata
|
<MessageMetadata
|
||||||
deletedForEveryone={deletedForEveryone}
|
deletedForEveryone={deletedForEveryone}
|
||||||
|
@ -628,7 +634,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
id={id}
|
id={id}
|
||||||
isShowingImage={this.isShowingImage()}
|
isShowingImage={this.isShowingImage()}
|
||||||
isSticker={isSticker}
|
isSticker={isStickerLike}
|
||||||
isTapToViewExpired={isTapToViewExpired}
|
isTapToViewExpired={isTapToViewExpired}
|
||||||
showMessageDetail={showMessageDetail}
|
showMessageDetail={showMessageDetail}
|
||||||
status={status}
|
status={status}
|
||||||
|
@ -2325,6 +2331,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
isTapToView,
|
isTapToView,
|
||||||
isTapToViewExpired,
|
isTapToViewExpired,
|
||||||
isTapToViewError,
|
isTapToViewError,
|
||||||
|
text,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { isSelected } = this.state;
|
const { isSelected } = this.state;
|
||||||
|
|
||||||
|
@ -2333,17 +2340,24 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
const width = this.getWidth();
|
const width = this.getWidth();
|
||||||
const isShowingImage = this.isShowingImage();
|
const isShowingImage = this.isShowingImage();
|
||||||
|
|
||||||
|
const isEmojiOnly =
|
||||||
|
text && isEmojiOnlyText(text) && getEmojiCount(text) < 6;
|
||||||
|
const isStickerLike = isSticker || isEmojiOnly;
|
||||||
|
|
||||||
const containerClassnames = classNames(
|
const containerClassnames = classNames(
|
||||||
'module-message__container',
|
'module-message__container',
|
||||||
isGIF(attachments) ? 'module-message__container--gif' : null,
|
isGIF(attachments) ? 'module-message__container--gif' : null,
|
||||||
isSelected && !isSticker ? 'module-message__container--selected' : null,
|
isSelected && !isStickerLike
|
||||||
isSticker ? 'module-message__container--with-sticker' : null,
|
? 'module-message__container--selected'
|
||||||
!isSticker ? `module-message__container--${direction}` : null,
|
: 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 ? 'module-message__container--with-tap-to-view' : null,
|
||||||
isTapToView && isTapToViewExpired
|
isTapToView && isTapToViewExpired
|
||||||
? 'module-message__container--with-tap-to-view-expired'
|
? 'module-message__container--with-tap-to-view-expired'
|
||||||
: null,
|
: null,
|
||||||
!isSticker && direction === 'outgoing'
|
!isStickerLike && direction === 'outgoing'
|
||||||
? `module-message__container--outgoing-${conversationColor}`
|
? `module-message__container--outgoing-${conversationColor}`
|
||||||
: null,
|
: null,
|
||||||
isTapToView && isAttachmentPending && !isTapToViewExpired
|
isTapToView && isAttachmentPending && !isTapToViewExpired
|
||||||
|
@ -2363,7 +2377,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
const containerStyles = {
|
const containerStyles = {
|
||||||
width: isShowingImage ? width : undefined,
|
width: isShowingImage ? width : undefined,
|
||||||
};
|
};
|
||||||
if (!isSticker && direction === 'outgoing') {
|
if (!isStickerLike && direction === 'outgoing') {
|
||||||
Object.assign(containerStyles, getCustomColorStyle(customColor));
|
Object.assign(containerStyles, getCustomColorStyle(customColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,13 @@ import * as log from '../../logging/log';
|
||||||
export const skinTones = ['1F3FB', '1F3FC', '1F3FD', '1F3FE', '1F3FF'];
|
export const skinTones = ['1F3FB', '1F3FC', '1F3FD', '1F3FE', '1F3FF'];
|
||||||
|
|
||||||
export type SkinToneKey = '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 = {
|
export type EmojiSkinVariation = {
|
||||||
unified: string;
|
unified: string;
|
||||||
|
@ -315,19 +321,22 @@ export function getSizeClass(str: string): SizeClassType {
|
||||||
|
|
||||||
const emojiCount = getEmojiCount(str);
|
const emojiCount = getEmojiCount(str);
|
||||||
|
|
||||||
if (emojiCount > 8) {
|
if (emojiCount === 1) {
|
||||||
return '';
|
return 'max';
|
||||||
}
|
}
|
||||||
if (emojiCount > 6) {
|
if (emojiCount === 2) {
|
||||||
return 'small';
|
return 'extra-large';
|
||||||
}
|
}
|
||||||
if (emojiCount > 4) {
|
if (emojiCount === 3) {
|
||||||
return 'medium';
|
|
||||||
}
|
|
||||||
if (emojiCount > 2) {
|
|
||||||
return 'large';
|
return 'large';
|
||||||
}
|
}
|
||||||
return 'jumbo';
|
if (emojiCount === 4) {
|
||||||
|
return 'medium';
|
||||||
|
}
|
||||||
|
if (emojiCount === 5) {
|
||||||
|
return 'small';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
data.forEach(emoji => {
|
data.forEach(emoji => {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import * as grapheme from './grapheme';
|
|
||||||
import { getEmojiCount } from '../components/emoji/lib';
|
import { getEmojiCount } from '../components/emoji/lib';
|
||||||
|
import { isEmojiOnlyText } from './isEmojiOnlyText';
|
||||||
|
|
||||||
type FontSizes = {
|
type FontSizes = {
|
||||||
diameter: number;
|
diameter: number;
|
||||||
|
@ -33,7 +33,7 @@ export function getFittedFontSize(
|
||||||
const sizes = getFontSizes(bubbleSize);
|
const sizes = getFontSizes(bubbleSize);
|
||||||
|
|
||||||
let candidateFontSize = sizes.text;
|
let candidateFontSize = sizes.text;
|
||||||
if (grapheme.count(text) === 1 && getEmojiCount(text) === 1) {
|
if (isEmojiOnlyText(text) && getEmojiCount(text) === 1) {
|
||||||
candidateFontSize = sizes.singleEmoji;
|
candidateFontSize = sizes.singleEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue