Pre-alpha: React with any emoji, behind flag

This commit is contained in:
Ken Powers 2020-05-05 15:49:34 -04:00 committed by Scott Nonnenberg
parent d13c3d3350
commit 0865a5481c
31 changed files with 572 additions and 234 deletions

View file

@ -1,6 +1,7 @@
import { take, uniq } from 'lodash';
import { EmojiPickDataType } from '../../components/emoji/EmojiPicker';
import dataInterface from '../../sql/Client';
import { useBoundActions } from '../../util/hooks';
const { updateEmojiUsage } = dataInterface;
@ -12,32 +13,34 @@ export type EmojisStateType = {
// Actions
type UseEmojiPayloadType = string;
type UseEmojiAction = {
type OnUseEmojiPayloadType = string;
type OnUseEmojiAction = {
type: 'emojis/USE_EMOJI';
payload: Promise<UseEmojiPayloadType>;
payload: Promise<OnUseEmojiPayloadType>;
};
type UseEmojiFulfilledAction = {
type OnUseEmojiFulfilledAction = {
type: 'emojis/USE_EMOJI_FULFILLED';
payload: UseEmojiPayloadType;
payload: OnUseEmojiPayloadType;
};
export type EmojisActionType = UseEmojiAction | UseEmojiFulfilledAction;
export type EmojisActionType = OnUseEmojiAction | OnUseEmojiFulfilledAction;
// Action Creators
export const actions = {
useEmoji,
onUseEmoji,
};
function useEmoji({ shortName }: EmojiPickDataType): UseEmojiAction {
export const useActions = () => useBoundActions(actions);
function onUseEmoji({ shortName }: EmojiPickDataType): OnUseEmojiAction {
return {
type: 'emojis/USE_EMOJI',
payload: doUseEmoji(shortName),
};
}
async function doUseEmoji(shortName: string): Promise<UseEmojiPayloadType> {
async function doUseEmoji(shortName: string): Promise<OnUseEmojiPayloadType> {
await updateEmojiUsage(shortName);
return shortName;

View file

@ -1,5 +1,10 @@
import { omit } from 'lodash';
import { createSelector } from 'reselect';
import { useSelector } from 'react-redux';
import { StateType } from '../reducer';
import * as storageShim from '../../shims/storage';
import { isShortName } from '../../components/emoji/lib';
import { useBoundActions } from '../../util/hooks';
// State
@ -53,6 +58,8 @@ export const actions = {
resetItems,
};
export const useActions = () => useBoundActions(actions);
function putItem(key: string, value: any): ItemPutAction {
storageShim.put(key, value);
@ -123,3 +130,12 @@ export function reducer(
return state;
}
// Selectors
const selectRecentEmojis = createSelector(
({ emojis }: StateType) => emojis.recents,
recents => recents.filter(isShortName)
);
export const useRecentEmojis = () => useSelector(selectRecentEmojis);

View file

@ -81,7 +81,7 @@ const dispatchPropsMap = {
mapDispatchToProps.removeItem('showStickersIntroduction'),
clearShowPickerHint: () =>
mapDispatchToProps.removeItem('showStickerPickerHint'),
onPickEmoji: mapDispatchToProps.useEmoji,
onPickEmoji: mapDispatchToProps.onUseEmoji,
};
const smart = connect(mapStateToProps, dispatchPropsMap);

View file

@ -0,0 +1,57 @@
import * as React from 'react';
import { useSelector } from 'react-redux';
import { get } from 'lodash';
import { StateType } from '../reducer';
import { useActions as useItemActions, useRecentEmojis } from '../ducks/items';
import { useActions as useEmojiActions } from '../ducks/emojis';
import {
EmojiPicker,
Props as EmojiPickerProps,
} from '../../components/emoji/EmojiPicker';
import { getIntl } from '../selectors/user';
import { LocalizerType } from '../../types/Util';
export const SmartEmojiPicker = React.forwardRef<
HTMLDivElement,
Pick<EmojiPickerProps, 'onPickEmoji' | 'onClose' | 'style'>
>(({ onPickEmoji, onClose, style }, ref) => {
const i18n = useSelector<StateType, LocalizerType>(getIntl);
const skinTone = useSelector<StateType, number>(state =>
get(state, ['items', 'skinTone'], 0)
);
const recentEmojis = useRecentEmojis();
const { putItem } = useItemActions();
const onSetSkinTone = React.useCallback(
tone => {
putItem('skinTone', tone);
},
[putItem]
);
const { onUseEmoji } = useEmojiActions();
const handlePickEmoji = React.useCallback(
data => {
onUseEmoji({ shortName: data.shortName });
onPickEmoji(data);
},
[onUseEmoji, onPickEmoji]
);
return (
<EmojiPicker
ref={ref}
i18n={i18n}
skinTone={skinTone}
onSetSkinTone={onSetSkinTone}
onPickEmoji={handlePickEmoji}
recentEmojis={recentEmojis}
onClose={onClose}
style={style}
/>
);
});

View file

@ -3,6 +3,7 @@ import React from 'react';
import { connect } from 'react-redux';
import { mapDispatchToProps } from '../actions';
import { Timeline } from '../../components/conversation/Timeline';
import { RenderEmojiPickerProps } from '../../components/conversation/ReactionPicker';
import { StateType } from '../reducer';
import { getIntl } from '../selectors/user';
@ -16,6 +17,7 @@ import { SmartTimelineItem } from './TimelineItem';
import { SmartTypingBubble } from './TypingBubble';
import { SmartLastSeenIndicator } from './LastSeenIndicator';
import { SmartTimelineLoadingRow } from './TimelineLoadingRow';
import { SmartEmojiPicker } from './EmojiPicker';
// Workaround: A react component's required properties are filtering up through connect()
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
@ -41,6 +43,22 @@ function renderItem(
{...actionProps}
conversationId={conversationId}
id={messageId}
renderEmojiPicker={renderEmojiPicker}
/>
);
}
function renderEmojiPicker({
ref,
onPickEmoji,
onClose,
style,
}: RenderEmojiPickerProps): JSX.Element {
return (
<SmartEmojiPicker
ref={ref}
onPickEmoji={onPickEmoji}
onClose={onClose}
style={style}
/>
);
}