mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-05-03 14:51:18 +01:00
Remove old emoji and sticker pickers
This commit is contained in:
@@ -117,7 +117,6 @@ const defaultMessageProps: TimelineMessagesProps = {
|
||||
previews: [],
|
||||
reactToMessage: action('default--reactToMessage'),
|
||||
readStatus: ReadStatus.Read,
|
||||
renderEmojiPicker: () => <div />,
|
||||
renderReactionPicker: () => <div />,
|
||||
renderAudioAttachment: () => <div>*AudioAttachment*</div>,
|
||||
setMessageToEdit: action('setMessageToEdit'),
|
||||
|
||||
@@ -6,29 +6,10 @@ import { action } from '@storybook/addon-actions';
|
||||
import type { Meta } from '@storybook/react';
|
||||
import type { Props as ReactionPickerProps } from './ReactionPicker.js';
|
||||
import { ReactionPicker } from './ReactionPicker.js';
|
||||
import { EmojiPicker } from '../emoji/EmojiPicker.js';
|
||||
import { DEFAULT_PREFERRED_REACTION_EMOJI } from '../../reactions/constants.js';
|
||||
import { EmojiSkinTone } from '../fun/data/emojis.js';
|
||||
|
||||
const { i18n } = window.SignalContext;
|
||||
|
||||
const renderEmojiPicker: ReactionPickerProps['renderEmojiPicker'] = ({
|
||||
onClose,
|
||||
onPickEmoji,
|
||||
onEmojiSkinToneDefaultChange,
|
||||
ref,
|
||||
}) => (
|
||||
<EmojiPicker
|
||||
i18n={i18n}
|
||||
emojiSkinToneDefault={EmojiSkinTone.None}
|
||||
ref={ref}
|
||||
onClose={onClose}
|
||||
onPickEmoji={onPickEmoji}
|
||||
onEmojiSkinToneDefaultChange={onEmojiSkinToneDefaultChange}
|
||||
wasInvokedFromKeyboard={false}
|
||||
/>
|
||||
);
|
||||
|
||||
export default {
|
||||
title: 'Components/Conversation/ReactionPicker',
|
||||
} satisfies Meta<ReactionPickerProps>;
|
||||
@@ -38,12 +19,7 @@ export function Base(): JSX.Element {
|
||||
<ReactionPicker
|
||||
i18n={i18n}
|
||||
onPick={action('onPick')}
|
||||
onEmojiSkinToneDefaultChange={action('onEmojiSkinToneDefaultChange')}
|
||||
openCustomizePreferredReactionsModal={action(
|
||||
'openCustomizePreferredReactionsModal'
|
||||
)}
|
||||
preferredReactionEmoji={DEFAULT_PREFERRED_REACTION_EMOJI}
|
||||
renderEmojiPicker={renderEmojiPicker}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -57,14 +33,7 @@ export function SelectedReaction(): JSX.Element {
|
||||
i18n={i18n}
|
||||
selected={e}
|
||||
onPick={action('onPick')}
|
||||
onEmojiSkinToneDefaultChange={action(
|
||||
'onEmojiSkinToneDefaultChange'
|
||||
)}
|
||||
openCustomizePreferredReactionsModal={action(
|
||||
'openCustomizePreferredReactionsModal'
|
||||
)}
|
||||
preferredReactionEmoji={DEFAULT_PREFERRED_REACTION_EMOJI}
|
||||
renderEmojiPicker={renderEmojiPicker}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -3,39 +3,24 @@
|
||||
|
||||
import React, { useCallback, useState, useEffect } from 'react';
|
||||
import { Button } from 'react-aria-components';
|
||||
import { convertShortName } from '../emoji/lib.js';
|
||||
import type { Props as EmojiPickerProps } from '../emoji/EmojiPicker.js';
|
||||
import { useDelayedRestoreFocus } from '../../hooks/useRestoreFocus.js';
|
||||
import type { LocalizerType, ThemeType } from '../../types/Util.js';
|
||||
import {
|
||||
ReactionPickerPicker,
|
||||
ReactionPickerPickerEmojiButton,
|
||||
ReactionPickerPickerMoreButton,
|
||||
ReactionPickerPickerStyle,
|
||||
} from '../ReactionPickerPicker.js';
|
||||
import type { EmojiSkinTone, EmojiVariantKey } from '../fun/data/emojis.js';
|
||||
import type { EmojiVariantKey } from '../fun/data/emojis.js';
|
||||
import { getEmojiVariantByKey } from '../fun/data/emojis.js';
|
||||
import { FunEmojiPicker } from '../fun/FunEmojiPicker.js';
|
||||
import type { FunEmojiSelection } from '../fun/panels/FunPanelEmojis.js';
|
||||
import { isFunPickerEnabled } from '../fun/isFunPickerEnabled.js';
|
||||
|
||||
export type RenderEmojiPickerProps = Pick<Props, 'onClose' | 'style'> &
|
||||
Pick<
|
||||
EmojiPickerProps,
|
||||
'onClickSettings' | 'onPickEmoji' | 'onEmojiSkinToneDefaultChange'
|
||||
> & {
|
||||
ref: React.Ref<HTMLDivElement>;
|
||||
};
|
||||
|
||||
export type OwnProps = {
|
||||
i18n: LocalizerType;
|
||||
selected?: string;
|
||||
onClose?: () => unknown;
|
||||
onPick: (emoji: string) => unknown;
|
||||
onEmojiSkinToneDefaultChange: (emojiSkinTone: EmojiSkinTone) => unknown;
|
||||
openCustomizePreferredReactionsModal?: () => unknown;
|
||||
preferredReactionEmoji: ReadonlyArray<string>;
|
||||
renderEmojiPicker: (props: RenderEmojiPickerProps) => React.ReactElement;
|
||||
theme?: ThemeType;
|
||||
messageEmojis?: ReadonlyArray<EmojiVariantKey>;
|
||||
};
|
||||
@@ -48,10 +33,7 @@ export const ReactionPicker = React.forwardRef<HTMLDivElement, Props>(
|
||||
i18n,
|
||||
onClose,
|
||||
onPick,
|
||||
onEmojiSkinToneDefaultChange,
|
||||
openCustomizePreferredReactionsModal,
|
||||
preferredReactionEmoji,
|
||||
renderEmojiPicker,
|
||||
selected,
|
||||
style,
|
||||
theme,
|
||||
@@ -80,14 +62,6 @@ export const ReactionPicker = React.forwardRef<HTMLDivElement, Props>(
|
||||
setEmojiPickerOpen(open);
|
||||
}, []);
|
||||
|
||||
// Handle EmojiPicker::onPickEmoji
|
||||
const onPickEmoji: EmojiPickerProps['onPickEmoji'] = React.useCallback(
|
||||
({ shortName, skinTone: pickedSkinTone }) => {
|
||||
onPick(convertShortName(shortName, pickedSkinTone));
|
||||
},
|
||||
[onPick]
|
||||
);
|
||||
|
||||
const onSelectEmoji = useCallback(
|
||||
(emojiSelection: FunEmojiSelection) => {
|
||||
const variant = getEmojiVariantByKey(emojiSelection.variantKey);
|
||||
@@ -99,17 +73,6 @@ export const ReactionPicker = React.forwardRef<HTMLDivElement, Props>(
|
||||
// Focus first button and restore focus on unmount
|
||||
const [focusRef] = useDelayedRestoreFocus();
|
||||
|
||||
if (!isFunPickerEnabled() && emojiPickerOpen) {
|
||||
return renderEmojiPicker({
|
||||
onClickSettings: openCustomizePreferredReactionsModal,
|
||||
onClose,
|
||||
onPickEmoji,
|
||||
onEmojiSkinToneDefaultChange,
|
||||
ref,
|
||||
style,
|
||||
});
|
||||
}
|
||||
|
||||
const otherSelected =
|
||||
selected != null && !preferredReactionEmoji.includes(selected);
|
||||
|
||||
@@ -149,32 +112,20 @@ export const ReactionPicker = React.forwardRef<HTMLDivElement, Props>(
|
||||
title={i18n('icu:Reactions--remove')}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{isFunPickerEnabled() && (
|
||||
<FunEmojiPicker
|
||||
open={emojiPickerOpen}
|
||||
onOpenChange={handleFunEmojiPickerOpenChange}
|
||||
onSelectEmoji={onSelectEmoji}
|
||||
theme={theme}
|
||||
showCustomizePreferredReactionsButton
|
||||
closeOnSelect
|
||||
messageEmojis={messageEmojis}
|
||||
>
|
||||
<Button
|
||||
aria-label={i18n('icu:Reactions--more')}
|
||||
className="module-ReactionPickerPicker__button module-ReactionPickerPicker__button--more"
|
||||
/>
|
||||
</FunEmojiPicker>
|
||||
)}
|
||||
{!isFunPickerEnabled() && (
|
||||
<ReactionPickerPickerMoreButton
|
||||
i18n={i18n}
|
||||
onClick={() => {
|
||||
setEmojiPickerOpen(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
<FunEmojiPicker
|
||||
open={emojiPickerOpen}
|
||||
onOpenChange={handleFunEmojiPickerOpenChange}
|
||||
onSelectEmoji={onSelectEmoji}
|
||||
theme={theme}
|
||||
showCustomizePreferredReactionsButton
|
||||
closeOnSelect
|
||||
messageEmojis={messageEmojis}
|
||||
>
|
||||
<Button
|
||||
aria-label={i18n('icu:Reactions--more')}
|
||||
className="module-ReactionPickerPicker__button module-ReactionPickerPicker__button--more"
|
||||
/>
|
||||
</FunEmojiPicker>
|
||||
)}
|
||||
</ReactionPickerPicker>
|
||||
);
|
||||
|
||||
@@ -10,11 +10,12 @@ import { Avatar } from '../Avatar.js';
|
||||
import { useRestoreFocus } from '../../hooks/useRestoreFocus.js';
|
||||
import type { ConversationType } from '../../state/ducks/conversations.js';
|
||||
import type { PreferredBadgeSelectorType } from '../../state/selectors/badges.js';
|
||||
import type { EmojiData } from '../emoji/lib.js';
|
||||
import { emojiToData } from '../emoji/lib.js';
|
||||
import { useEscapeHandling } from '../../hooks/useEscapeHandling.js';
|
||||
import type { ThemeType } from '../../types/Util.js';
|
||||
import type { EmojiParentKey, EmojiVariantKey } from '../fun/data/emojis.js';
|
||||
import {
|
||||
EMOJI_PARENT_KEY_CONSTANTS,
|
||||
getEmojiParentKeyByVariantKey,
|
||||
getEmojiVariantByKey,
|
||||
getEmojiVariantKeyByValue,
|
||||
isEmojiVariantValue,
|
||||
@@ -23,7 +24,7 @@ import { strictAssert } from '../../util/assert.js';
|
||||
import { FunStaticEmoji } from '../fun/FunEmoji.js';
|
||||
import { useFunEmojiLocalizer } from '../fun/useFunEmojiLocalizer.js';
|
||||
|
||||
const { groupBy, mapValues, orderBy } = lodash;
|
||||
const { mapValues, orderBy } = lodash;
|
||||
|
||||
export type Reaction = {
|
||||
emoji: string;
|
||||
@@ -56,13 +57,13 @@ export type Props = OwnProps &
|
||||
Pick<AvatarProps, 'i18n'>;
|
||||
|
||||
const DEFAULT_EMOJI_ORDER = [
|
||||
'heart',
|
||||
'+1',
|
||||
'-1',
|
||||
'joy',
|
||||
'open_mouth',
|
||||
'cry',
|
||||
'rage',
|
||||
EMOJI_PARENT_KEY_CONSTANTS.RED_HEART,
|
||||
EMOJI_PARENT_KEY_CONSTANTS.THUMBS_UP,
|
||||
EMOJI_PARENT_KEY_CONSTANTS.THUMBS_DOWN,
|
||||
EMOJI_PARENT_KEY_CONSTANTS.FACE_WITH_TEARS_OF_JOY,
|
||||
EMOJI_PARENT_KEY_CONSTANTS.FACE_WITH_OPEN_MOUTH,
|
||||
EMOJI_PARENT_KEY_CONSTANTS.CRYING_FACE,
|
||||
EMOJI_PARENT_KEY_CONSTANTS.ENRAGED_FACE,
|
||||
];
|
||||
|
||||
type ReactionCategory = {
|
||||
@@ -72,7 +73,11 @@ type ReactionCategory = {
|
||||
index: number;
|
||||
};
|
||||
|
||||
type ReactionWithEmojiData = Reaction & EmojiData;
|
||||
type ReactionWithEmojiData = Reaction &
|
||||
Readonly<{
|
||||
parentKey: EmojiParentKey;
|
||||
variantKey: EmojiVariantKey;
|
||||
}>;
|
||||
|
||||
function ReactionViewerEmoji(props: {
|
||||
emojiVariantValue: string | undefined;
|
||||
@@ -112,37 +117,32 @@ export const ReactionViewer = React.forwardRef<HTMLDivElement, Props>(
|
||||
() =>
|
||||
reactions
|
||||
.map(reaction => {
|
||||
const emojiData = emojiToData(reaction.emoji);
|
||||
|
||||
if (!emojiData) {
|
||||
return undefined;
|
||||
if (!isEmojiVariantValue(reaction.emoji)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...reaction,
|
||||
...emojiData,
|
||||
};
|
||||
const variantKey = getEmojiVariantKeyByValue(reaction.emoji);
|
||||
const parentKey = getEmojiParentKeyByVariantKey(variantKey);
|
||||
return { ...reaction, parentKey, variantKey };
|
||||
})
|
||||
.filter(
|
||||
(
|
||||
reactionWithEmojiData
|
||||
): reactionWithEmojiData is ReactionWithEmojiData =>
|
||||
Boolean(reactionWithEmojiData)
|
||||
),
|
||||
.filter((data): data is ReactionWithEmojiData => {
|
||||
return data != null;
|
||||
}),
|
||||
[reactions]
|
||||
);
|
||||
|
||||
const groupedAndSortedReactions = React.useMemo(
|
||||
() =>
|
||||
mapValues(
|
||||
{
|
||||
all: reactionsWithEmojiData,
|
||||
...groupBy(reactionsWithEmojiData, 'short_name'),
|
||||
},
|
||||
groupedReactions => orderBy(groupedReactions, ['timestamp'], ['desc'])
|
||||
),
|
||||
[reactionsWithEmojiData]
|
||||
);
|
||||
const groupedAndSortedReactions = React.useMemo(() => {
|
||||
const groups = Object.groupBy(reactionsWithEmojiData, data => {
|
||||
return data.parentKey;
|
||||
});
|
||||
|
||||
return mapValues(
|
||||
{
|
||||
all: reactionsWithEmojiData,
|
||||
...groups,
|
||||
},
|
||||
groupedReactions => orderBy(groupedReactions, ['timestamp'], ['desc'])
|
||||
);
|
||||
}, [reactionsWithEmojiData]);
|
||||
|
||||
const reactionCategories: Array<ReactionCategory> = React.useMemo(
|
||||
() =>
|
||||
@@ -159,9 +159,9 @@ export const ReactionViewer = React.forwardRef<HTMLDivElement, Props>(
|
||||
const localUserReaction = groupedReactions.find(r => r.from.isMe);
|
||||
const firstReaction = localUserReaction || groupedReactions[0];
|
||||
return {
|
||||
id: firstReaction.short_name,
|
||||
index: DEFAULT_EMOJI_ORDER.includes(firstReaction.short_name)
|
||||
? DEFAULT_EMOJI_ORDER.indexOf(firstReaction.short_name)
|
||||
id: firstReaction.parentKey,
|
||||
index: DEFAULT_EMOJI_ORDER.includes(firstReaction.parentKey)
|
||||
? DEFAULT_EMOJI_ORDER.indexOf(firstReaction.parentKey)
|
||||
: Infinity,
|
||||
emoji: firstReaction.emoji,
|
||||
count: groupedReactions.length,
|
||||
|
||||
@@ -376,7 +376,6 @@ const renderItem = ({
|
||||
item={items[messageId]}
|
||||
renderAudioAttachment={() => <div>*AudioAttachment*</div>}
|
||||
renderContact={() => <div>*ContactName*</div>}
|
||||
renderEmojiPicker={() => <div />}
|
||||
renderReactionPicker={() => <div />}
|
||||
renderUniversalTimerNotification={() => (
|
||||
<div>*UniversalTimerNotification*</div>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import * as React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { Meta } from '@storybook/react';
|
||||
import { EmojiPicker } from '../emoji/EmojiPicker.js';
|
||||
import { DurationInSeconds } from '../../util/durations/index.js';
|
||||
import type { PropsType as TimelineItemProps } from './TimelineItem.js';
|
||||
import { TimelineItem } from './TimelineItem.js';
|
||||
@@ -16,28 +15,9 @@ import { WidthBreakpoint } from '../_util.js';
|
||||
import { ThemeType } from '../../types/Util.js';
|
||||
import { PaymentEventKind } from '../../types/Payment.js';
|
||||
import { ErrorBoundary } from './ErrorBoundary.js';
|
||||
import { EmojiSkinTone } from '../fun/data/emojis.js';
|
||||
|
||||
const { i18n } = window.SignalContext;
|
||||
|
||||
const renderEmojiPicker: TimelineItemProps['renderEmojiPicker'] = ({
|
||||
onClose,
|
||||
onPickEmoji,
|
||||
ref,
|
||||
}) => (
|
||||
<EmojiPicker
|
||||
i18n={i18n}
|
||||
emojiSkinToneDefault={EmojiSkinTone.None}
|
||||
onEmojiSkinToneDefaultChange={action(
|
||||
'EmojiPicker::onEmojiSkinToneDefaultChange'
|
||||
)}
|
||||
ref={ref}
|
||||
onClose={onClose}
|
||||
onPickEmoji={onPickEmoji}
|
||||
wasInvokedFromKeyboard={false}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderReactionPicker: TimelineItemProps['renderReactionPicker'] = () => (
|
||||
<div />
|
||||
);
|
||||
@@ -124,7 +104,6 @@ const getDefaultProps = () => ({
|
||||
|
||||
renderContact,
|
||||
renderUniversalTimerNotification,
|
||||
renderEmojiPicker,
|
||||
renderReactionPicker,
|
||||
renderAudioAttachment: () => <div>*AudioAttachment*</div>,
|
||||
viewStory: action('viewStory'),
|
||||
|
||||
@@ -210,7 +210,6 @@ export type PropsType = PropsLocalType &
|
||||
AllMessageProps,
|
||||
| 'containerWidthBreakpoint'
|
||||
| 'getPreferredBadge'
|
||||
| 'renderEmojiPicker'
|
||||
| 'renderAudioAttachment'
|
||||
| 'renderReactionPicker'
|
||||
| 'shouldCollapseAbove'
|
||||
|
||||
@@ -9,7 +9,6 @@ import type { Meta, StoryFn } from '@storybook/react';
|
||||
|
||||
import { SignalService } from '../../protobuf/index.js';
|
||||
import { ConversationColors } from '../../types/Colors.js';
|
||||
import { EmojiPicker } from '../emoji/EmojiPicker.js';
|
||||
import type { AudioAttachmentProps } from './Message.js';
|
||||
import type { Props } from './TimelineMessage.js';
|
||||
import { TimelineMessage } from './TimelineMessage.js';
|
||||
@@ -44,7 +43,6 @@ import { getFakeBadge } from '../../test-helpers/getFakeBadge.js';
|
||||
import { ThemeType } from '../../types/Util.js';
|
||||
import { BadgeCategory } from '../../badges/BadgeCategory.js';
|
||||
import { PaymentEventKind } from '../../types/Payment.js';
|
||||
import { EmojiSkinTone } from '../fun/data/emojis.js';
|
||||
|
||||
const { isBoolean, noop } = lodash;
|
||||
|
||||
@@ -110,24 +108,6 @@ function getJoyReaction() {
|
||||
};
|
||||
}
|
||||
|
||||
const renderEmojiPicker: Props['renderEmojiPicker'] = ({
|
||||
onClose,
|
||||
onPickEmoji,
|
||||
ref,
|
||||
}) => (
|
||||
<EmojiPicker
|
||||
i18n={i18n}
|
||||
emojiSkinToneDefault={EmojiSkinTone.None}
|
||||
onEmojiSkinToneDefaultChange={action(
|
||||
'EmojiPicker::onEmojiSkinToneDefaultChange'
|
||||
)}
|
||||
ref={ref}
|
||||
onClose={onClose}
|
||||
onPickEmoji={onPickEmoji}
|
||||
wasInvokedFromKeyboard={false}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderReactionPicker: Props['renderReactionPicker'] = () => <div />;
|
||||
|
||||
/**
|
||||
@@ -319,7 +299,6 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||
overrideProps.readStatus === undefined
|
||||
? ReadStatus.Read
|
||||
: overrideProps.readStatus,
|
||||
renderEmojiPicker,
|
||||
renderReactionPicker,
|
||||
renderAudioAttachment,
|
||||
saveAttachment: action('saveAttachment'),
|
||||
|
||||
@@ -31,7 +31,6 @@ import type {
|
||||
} from './Message.js';
|
||||
import type { PushPanelForConversationActionType } from '../../state/ducks/conversations.js';
|
||||
import { doesMessageBodyOverflow } from './MessageBodyReadMore.js';
|
||||
import type { Props as ReactionPickerProps } from './ReactionPicker.js';
|
||||
import {
|
||||
useKeyboardShortcutsConditionally,
|
||||
useOpenContextMenu,
|
||||
@@ -90,8 +89,7 @@ export type PropsActions = {
|
||||
|
||||
export type Props = PropsData &
|
||||
PropsActions &
|
||||
Omit<PropsHousekeeping, 'isAttachmentPending'> &
|
||||
Pick<ReactionPickerProps, 'renderEmojiPicker'> & {
|
||||
Omit<PropsHousekeeping, 'isAttachmentPending'> & {
|
||||
renderReactionPicker: (
|
||||
props: React.ComponentProps<typeof SmartReactionPicker>
|
||||
) => JSX.Element;
|
||||
@@ -123,7 +121,6 @@ export function TimelineMessage(props: Props): JSX.Element {
|
||||
copyMessageText,
|
||||
pushPanelForConversation,
|
||||
reactToMessage,
|
||||
renderEmojiPicker,
|
||||
renderReactionPicker,
|
||||
retryDeleteForEveryone,
|
||||
retryMessageSend,
|
||||
@@ -343,7 +340,6 @@ export function TimelineMessage(props: Props): JSX.Element {
|
||||
remove: emoji === selectedReaction,
|
||||
});
|
||||
},
|
||||
renderEmojiPicker,
|
||||
messageEmojis,
|
||||
})
|
||||
}
|
||||
@@ -369,7 +365,6 @@ export function TimelineMessage(props: Props): JSX.Element {
|
||||
renderReactionPicker,
|
||||
selectedReaction,
|
||||
reactToMessage,
|
||||
renderEmojiPicker,
|
||||
toggleReactionPicker,
|
||||
id,
|
||||
messageEmojis,
|
||||
|
||||
Reference in New Issue
Block a user