From 8b81838f2f4daa3c98ac365261f82deee676d44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 18 Mar 2023 00:07:18 +0100 Subject: [PATCH 1/4] Support custom emoji reacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/emoji-reacts.ts | 11 ++++++----- app/soapbox/components/account.tsx | 3 +++ app/soapbox/components/status-action-bar.tsx | 9 +++++---- app/soapbox/components/status-action-button.tsx | 6 ++++-- app/soapbox/components/status-reaction-wrapper.tsx | 6 +++--- .../components/ui/emoji-selector/emoji-selector.tsx | 11 ++++++----- app/soapbox/components/ui/emoji/emoji.tsx | 4 ++-- app/soapbox/features/emoji/index.ts | 2 +- .../status/components/status-interaction-bar.tsx | 1 + .../features/ui/components/modals/reactions-modal.tsx | 6 +++--- app/soapbox/reducers/statuses.ts | 2 +- app/soapbox/reducers/user-lists.ts | 1 + app/soapbox/utils/emoji-reacts.ts | 10 ++++++---- app/soapbox/utils/features.ts | 5 +++++ 14 files changed, 47 insertions(+), 30 deletions(-) diff --git a/app/soapbox/actions/emoji-reacts.ts b/app/soapbox/actions/emoji-reacts.ts index ac205d38d..746a7372f 100644 --- a/app/soapbox/actions/emoji-reacts.ts +++ b/app/soapbox/actions/emoji-reacts.ts @@ -25,7 +25,7 @@ const EMOJI_REACTS_FETCH_FAIL = 'EMOJI_REACTS_FETCH_FAIL'; const noOp = () => () => new Promise(f => f(undefined)); -const simpleEmojiReact = (status: Status, emoji: string) => +const simpleEmojiReact = (status: Status, emoji: string, custom?: string) => (dispatch: AppDispatch) => { const emojiReacts: ImmutableList> = status.pleroma.get('emoji_reactions') || ImmutableList(); @@ -43,7 +43,7 @@ const simpleEmojiReact = (status: Status, emoji: string) => if (emoji === '👍') { dispatch(favourite(status)); } else { - dispatch(emojiReact(status, emoji)); + dispatch(emojiReact(status, emoji, custom)); } }).catch(err => { console.error(err); @@ -70,11 +70,11 @@ const fetchEmojiReacts = (id: string, emoji: string) => }); }; -const emojiReact = (status: Status, emoji: string) => +const emojiReact = (status: Status, emoji: string, custom?: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return dispatch(noOp()); - dispatch(emojiReactRequest(status, emoji)); + dispatch(emojiReactRequest(status, emoji, custom)); return api(getState) .put(`/api/v1/pleroma/statuses/${status.get('id')}/reactions/${emoji}`) @@ -120,10 +120,11 @@ const fetchEmojiReactsFail = (id: string, error: AxiosError) => ({ error, }); -const emojiReactRequest = (status: Status, emoji: string) => ({ +const emojiReactRequest = (status: Status, emoji: string, custom?: string) => ({ type: EMOJI_REACT_REQUEST, status, emoji, + custom, skipLoading: true, }); diff --git a/app/soapbox/components/account.tsx b/app/soapbox/components/account.tsx index f79366895..20150b79d 100644 --- a/app/soapbox/components/account.tsx +++ b/app/soapbox/components/account.tsx @@ -91,6 +91,7 @@ export interface IAccount { showEdit?: boolean approvalStatus?: StatusApprovalStatus emoji?: string + emojiUrl?: string note?: string } @@ -116,6 +117,7 @@ const Account = ({ showEdit = false, approvalStatus, emoji, + emojiUrl, note, }: IAccount) => { const overflowRef = useRef(null); @@ -193,6 +195,7 @@ const Account = ({ )} diff --git a/app/soapbox/components/status-action-bar.tsx b/app/soapbox/components/status-action-bar.tsx index 8e8d9c202..f36bb34e9 100644 --- a/app/soapbox/components/status-action-bar.tsx +++ b/app/soapbox/components/status-action-bar.tsx @@ -538,7 +538,8 @@ const StatusActionBar: React.FC = ({ allowedEmoji, ).reduce((acc, cur) => acc + cur.get('count'), 0); - const meEmojiReact = getReactForStatus(status, allowedEmoji) as keyof typeof reactMessages | undefined; + const meEmojiReact = getReactForStatus(status, allowedEmoji); + const meEmojiName = meEmojiReact?.get('name') as keyof typeof reactMessages | undefined; const reactMessages = { '👍': messages.reactionLike, @@ -550,7 +551,7 @@ const StatusActionBar: React.FC = ({ '': messages.favourite, }; - const meEmojiTitle = intl.formatMessage(reactMessages[meEmojiReact || ''] || messages.favourite); + const meEmojiTitle = intl.formatMessage(reactMessages[meEmojiName || ''] || messages.favourite); const menu = _makeMenu(publicStatus); let reblogIcon = require('@tabler/icons/repeat.svg'); @@ -635,7 +636,7 @@ const StatusActionBar: React.FC = ({ icon={require('@tabler/icons/heart.svg')} filled color='accent' - active={Boolean(meEmojiReact)} + active={Boolean(meEmojiName)} count={emojiReactCount} emoji={meEmojiReact} text={withLabels ? meEmojiTitle : undefined} @@ -648,7 +649,7 @@ const StatusActionBar: React.FC = ({ color='accent' filled onClick={handleFavouriteClick} - active={Boolean(meEmojiReact)} + active={Boolean(meEmojiName)} count={favouriteCount} text={withLabels ? meEmojiTitle : undefined} /> diff --git a/app/soapbox/components/status-action-button.tsx b/app/soapbox/components/status-action-button.tsx index 6fe7130f6..39795fc7e 100644 --- a/app/soapbox/components/status-action-button.tsx +++ b/app/soapbox/components/status-action-button.tsx @@ -4,6 +4,8 @@ import React from 'react'; import { Text, Icon, Emoji } from 'soapbox/components/ui'; import { shortNumberFormat } from 'soapbox/utils/numbers'; +import type { Map as ImmutableMap } from 'immutable'; + const COLORS = { accent: 'accent', success: 'success', @@ -31,7 +33,7 @@ interface IStatusActionButton extends React.ButtonHTMLAttributes text?: React.ReactNode } @@ -42,7 +44,7 @@ const StatusActionButton = React.forwardRef - + ); } else { diff --git a/app/soapbox/components/status-reaction-wrapper.tsx b/app/soapbox/components/status-reaction-wrapper.tsx index b95d98375..206cd1fed 100644 --- a/app/soapbox/components/status-reaction-wrapper.tsx +++ b/app/soapbox/components/status-reaction-wrapper.tsx @@ -60,9 +60,9 @@ const StatusReactionWrapper: React.FC = ({ statusId, chi } }; - const handleReact = (emoji: string): void => { + const handleReact = (emoji: string, custom?: string): void => { if (ownAccount) { - dispatch(simpleEmojiReact(status, emoji)); + dispatch(simpleEmojiReact(status, emoji, custom)); } else { handleUnauthorized(); } @@ -71,7 +71,7 @@ const StatusReactionWrapper: React.FC = ({ statusId, chi }; const handleClick: React.EventHandler = e => { - const meEmojiReact = getReactForStatus(status, soapboxConfig.allowedEmoji) || '👍'; + const meEmojiReact = getReactForStatus(status, soapboxConfig.allowedEmoji)?.get('name') || '👍'; if (isUserTouching()) { if (ownAccount) { diff --git a/app/soapbox/components/ui/emoji-selector/emoji-selector.tsx b/app/soapbox/components/ui/emoji-selector/emoji-selector.tsx index 961155cb3..55e98b048 100644 --- a/app/soapbox/components/ui/emoji-selector/emoji-selector.tsx +++ b/app/soapbox/components/ui/emoji-selector/emoji-selector.tsx @@ -5,9 +5,9 @@ import { usePopper } from 'react-popper'; import { Emoji as EmojiComponent, HStack, IconButton } from 'soapbox/components/ui'; import EmojiPickerDropdown from 'soapbox/features/emoji/components/emoji-picker-dropdown'; -import { useSoapboxConfig } from 'soapbox/hooks'; +import { useFeatures, useSoapboxConfig } from 'soapbox/hooks'; -import type { Emoji, NativeEmoji } from 'soapbox/features/emoji'; +import type { Emoji } from 'soapbox/features/emoji'; interface IEmojiButton { /** Unicode emoji character. */ @@ -39,7 +39,7 @@ const EmojiButton: React.FC = ({ emoji, className, onClick, tabInd interface IEmojiSelector { onClose?(): void /** Event handler when an emoji is clicked. */ - onReact(emoji: string): void + onReact(emoji: string, custom?: string): void /** Element that triggers the EmojiSelector Popper */ referenceElement: HTMLElement | null placement?: Placement @@ -62,6 +62,7 @@ const EmojiSelector: React.FC = ({ all = true, }): JSX.Element => { const soapboxConfig = useSoapboxConfig(); + const { customEmojiReacts } = useFeatures(); const [expanded, setExpanded] = useState(false); @@ -102,7 +103,7 @@ const EmojiSelector: React.FC = ({ }; const handlePickEmoji = (emoji: Emoji) => { - onReact((emoji as NativeEmoji).native); + onReact(emoji.custom ? emoji.id : emoji.native, emoji.custom ? emoji.imageUrl : undefined); }; useEffect(() => () => { @@ -148,7 +149,7 @@ const EmojiSelector: React.FC = ({ visible={expanded} setVisible={setExpanded} update={update} - withCustom={false} + withCustom={customEmojiReacts} onPickEmoji={handlePickEmoji} /> ) : ( diff --git a/app/soapbox/components/ui/emoji/emoji.tsx b/app/soapbox/components/ui/emoji/emoji.tsx index b631f230d..eb8f02509 100644 --- a/app/soapbox/components/ui/emoji/emoji.tsx +++ b/app/soapbox/components/ui/emoji/emoji.tsx @@ -10,7 +10,7 @@ interface IEmoji extends React.ImgHTMLAttributes { /** A single emoji image. */ const Emoji: React.FC = (props): JSX.Element | null => { - const { emoji, alt, ...rest } = props; + const { emoji, alt, src, ...rest } = props; const codepoints = toCodePoints(removeVS16s(emoji)); const filename = codepoints.join('-'); @@ -20,7 +20,7 @@ const Emoji: React.FC = (props): JSX.Element | null => { {alt ); diff --git a/app/soapbox/features/emoji/index.ts b/app/soapbox/features/emoji/index.ts index 8fa279dad..61a957e9e 100644 --- a/app/soapbox/features/emoji/index.ts +++ b/app/soapbox/features/emoji/index.ts @@ -28,7 +28,7 @@ export interface CustomEmoji { export interface NativeEmoji { id: string colons: string - custom?: boolean + custom?: false unified: string native: string } diff --git a/app/soapbox/features/status/components/status-interaction-bar.tsx b/app/soapbox/features/status/components/status-interaction-bar.tsx index fb4db1466..4d0333c06 100644 --- a/app/soapbox/features/status/components/status-interaction-bar.tsx +++ b/app/soapbox/features/status/components/status-interaction-bar.tsx @@ -154,6 +154,7 @@ const StatusInteractionBar: React.FC = ({ status }): JSX. key={i} className='h-4.5 w-4.5 flex-none' emoji={e.get('name')} + src={e.get('url')} /> ); })} diff --git a/app/soapbox/features/ui/components/modals/reactions-modal.tsx b/app/soapbox/features/ui/components/modals/reactions-modal.tsx index a31957745..e5fbca99f 100644 --- a/app/soapbox/features/ui/components/modals/reactions-modal.tsx +++ b/app/soapbox/features/ui/components/modals/reactions-modal.tsx @@ -54,7 +54,7 @@ const ReactionsModal: React.FC = ({ onClose, statusId, reaction reactions!.forEach(reaction => items.push( { text:
- + {reaction.count}
, action: () => setReaction(reaction.name), @@ -71,7 +71,7 @@ const ReactionsModal: React.FC = ({ onClose, statusId, reaction const accounts = reactions && (reaction ? reactions.find(({ name }) => name === reaction)?.accounts.map(account => ({ id: account, reaction: reaction })) - : reactions.map(({ accounts, name }) => accounts.map(account => ({ id: account, reaction: name }))).flatten()) as ImmutableList<{ id: string, reaction: string }>; + : reactions.map(({ accounts, name, url }) => accounts.map(account => ({ id: account, reaction: name, reactionUrl: url }))).flatten()) as ImmutableList<{ id: string, reaction: string, reactionUrl?: string }>; let body; @@ -91,7 +91,7 @@ const ReactionsModal: React.FC = ({ onClose, statusId, reaction itemClassName='pb-3' > {accounts.map((account) => - , + , )} ); diff --git a/app/soapbox/reducers/statuses.ts b/app/soapbox/reducers/statuses.ts index 66b34b7d7..11e4846a6 100644 --- a/app/soapbox/reducers/statuses.ts +++ b/app/soapbox/reducers/statuses.ts @@ -242,7 +242,7 @@ export default function statuses(state = initialState, action: AnyAction): State return state .updateIn( [action.status.get('id'), 'pleroma', 'emoji_reactions'], - emojiReacts => simulateEmojiReact(emojiReacts as any, action.emoji), + emojiReacts => simulateEmojiReact(emojiReacts as any, action.emoji, action.custom), ); case UNEMOJI_REACT_REQUEST: return state diff --git a/app/soapbox/reducers/user-lists.ts b/app/soapbox/reducers/user-lists.ts index 206cd7c81..c979e9c46 100644 --- a/app/soapbox/reducers/user-lists.ts +++ b/app/soapbox/reducers/user-lists.ts @@ -82,6 +82,7 @@ export const ReactionRecord = ImmutableRecord({ accounts: ImmutableOrderedSet(), count: 0, name: '', + url: null as string | null, }); const ReactionListRecord = ImmutableRecord({ diff --git a/app/soapbox/utils/emoji-reacts.ts b/app/soapbox/utils/emoji-reacts.ts index b9104e783..2fa83906b 100644 --- a/app/soapbox/utils/emoji-reacts.ts +++ b/app/soapbox/utils/emoji-reacts.ts @@ -74,19 +74,19 @@ export const reduceEmoji = (emojiReacts: ImmutableList, favouritesCo allowedEmoji, )); -export const getReactForStatus = (status: any, allowedEmoji = ALLOWED_EMOJI): string | undefined => { +export const getReactForStatus = (status: any, allowedEmoji = ALLOWED_EMOJI): EmojiReact | undefined => { const result = reduceEmoji( status.pleroma.get('emoji_reactions', ImmutableList()), status.favourites_count || 0, status.favourited, allowedEmoji, ).filter(e => e.get('me') === true) - .getIn([0, 'name']); + .get(0); - return typeof result === 'string' ? result : undefined; + return typeof result?.get('name') === 'string' ? result : undefined; }; -export const simulateEmojiReact = (emojiReacts: ImmutableList, emoji: string) => { +export const simulateEmojiReact = (emojiReacts: ImmutableList, emoji: string, url?: string) => { const idx = emojiReacts.findIndex(e => e.get('name') === emoji); const emojiReact = emojiReacts.get(idx); @@ -94,12 +94,14 @@ export const simulateEmojiReact = (emojiReacts: ImmutableList, emoji return emojiReacts.set(idx, emojiReact.merge({ count: emojiReact.get('count') + 1, me: true, + url, })); } else { return emojiReacts.push(ImmutableMap({ count: 1, me: true, name: emoji, + url, })); } }; diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index 6fe794b57..e99d4c921 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -324,6 +324,11 @@ const getInstanceFeatures = (instance: Instance) => { v.software === TAKAHE, ]), + /** + * Ability to add non-standard reactions to a status. + */ + customEmojiReacts: v.software === PLEROMA && gte(v.version, '2.5.50'), + /** * Legacy DMs timeline where messages are displayed chronologically without groupings. * @see GET /api/v1/timelines/direct From 179eb7fc993d2d7322c1069339e7658445aebda0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 18 Mar 2023 11:55:00 +0100 Subject: [PATCH 2/4] Fix preview in reactions modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../notifications/components/notification.tsx | 1 + .../ui/components/modals/reactions-modal.tsx | 26 ++++++++++++++----- app/soapbox/normalizers/notification.ts | 1 + 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/soapbox/features/notifications/components/notification.tsx b/app/soapbox/features/notifications/components/notification.tsx index 5df21c647..6812cdeb8 100644 --- a/app/soapbox/features/notifications/components/notification.tsx +++ b/app/soapbox/features/notifications/components/notification.tsx @@ -269,6 +269,7 @@ const Notification: React.FC = (props) => { return ( ); diff --git a/app/soapbox/features/ui/components/modals/reactions-modal.tsx b/app/soapbox/features/ui/components/modals/reactions-modal.tsx index e5fbca99f..08e4afa43 100644 --- a/app/soapbox/features/ui/components/modals/reactions-modal.tsx +++ b/app/soapbox/features/ui/components/modals/reactions-modal.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; import { List as ImmutableList } from 'immutable'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { fetchFavourites, fetchReactions } from 'soapbox/actions/interactions'; @@ -17,6 +17,12 @@ const messages = defineMessages({ all: { id: 'reactions.all', defaultMessage: 'All' }, }); +interface IAccountWithReaction { + id: string + reaction: string + reactionUrl?: string +} + interface IReactionsModal { onClose: (string: string) => void statusId: string @@ -65,17 +71,25 @@ const ReactionsModal: React.FC = ({ onClose, statusId, reaction return ; }; + const accounts = useMemo((): ImmutableList | undefined => { + if (!reactions) return; + + if (reaction) { + const reactionRecord = reactions.find(({ name }) => name === reaction); + + if (reactionRecord) return reactionRecord.accounts.map(account => ({ id: account, reaction: reaction, reactionUrl: reactionRecord.url || undefined })).toList(); + } else { + return reactions.map(({ accounts, name, url }) => accounts.map(account => ({ id: account, reaction: name, reactionUrl: url }))).flatten() as ImmutableList; + } + }, [reactions, reaction]); + useEffect(() => { fetchData(); }, []); - const accounts = reactions && (reaction - ? reactions.find(({ name }) => name === reaction)?.accounts.map(account => ({ id: account, reaction: reaction })) - : reactions.map(({ accounts, name, url }) => accounts.map(account => ({ id: account, reaction: name, reactionUrl: url }))).flatten()) as ImmutableList<{ id: string, reaction: string, reactionUrl?: string }>; - let body; - if (!accounts) { + if (!accounts || !reactions) { body = ; } else { const emptyMessage = ; diff --git a/app/soapbox/normalizers/notification.ts b/app/soapbox/normalizers/notification.ts index 9b34278b6..2412db05d 100644 --- a/app/soapbox/normalizers/notification.ts +++ b/app/soapbox/normalizers/notification.ts @@ -17,6 +17,7 @@ export const NotificationRecord = ImmutableRecord({ chat_message: null as ImmutableMap | string | null, // pleroma:chat_mention created_at: new Date(), emoji: null as string | null, // pleroma:emoji_reaction + emoji_url: null as string | null, // pleroma:emoji_reaction id: '', status: null as EmbeddedEntity, target: null as EmbeddedEntity, // move From 09a0a3693597976293610f9088ba2c29f83aae42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 18 Mar 2023 13:27:27 +0100 Subject: [PATCH 3/4] Update tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../utils/__tests__/emoji-reacts.test.ts | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/soapbox/utils/__tests__/emoji-reacts.test.ts b/app/soapbox/utils/__tests__/emoji-reacts.test.ts index f5c6f150c..9b901614c 100644 --- a/app/soapbox/utils/__tests__/emoji-reacts.test.ts +++ b/app/soapbox/utils/__tests__/emoji-reacts.test.ts @@ -52,15 +52,15 @@ describe('mergeEmojiFavourites', () => { describe('with existing 👍 reacts', () => { const emojiReacts = fromJS([ - { 'count': 20, 'me': false, 'name': '👍' }, - { 'count': 15, 'me': false, 'name': '❤' }, - { 'count': 7, 'me': false, 'name': '😯' }, + { 'count': 20, 'me': false, 'name': '👍', 'url': undefined }, + { 'count': 15, 'me': false, 'name': '❤', 'url': undefined }, + { 'count': 7, 'me': false, 'name': '😯', 'url': undefined }, ]) as ImmutableList>; it('combines 👍 reacts with favourites', () => { expect(mergeEmojiFavourites(emojiReacts, favouritesCount, favourited)).toEqual(fromJS([ - { 'count': 32, 'me': true, 'name': '👍' }, - { 'count': 15, 'me': false, 'name': '❤' }, - { 'count': 7, 'me': false, 'name': '😯' }, + { 'count': 32, 'me': true, 'name': '👍', 'url': undefined }, + { 'count': 15, 'me': false, 'name': '❤', 'url': undefined }, + { 'count': 7, 'me': false, 'name': '😯', 'url': undefined }, ])); }); }); @@ -146,12 +146,12 @@ describe('getReactForStatus', () => { ], }, })); - expect(getReactForStatus(status, ALLOWED_EMOJI)).toEqual('❤'); + expect(getReactForStatus(status, ALLOWED_EMOJI)?.get('name')).toEqual('❤'); }); it('returns a thumbs-up for a favourite', () => { const status = normalizeStatus(fromJS({ favourites_count: 1, favourited: true })); - expect(getReactForStatus(status)).toEqual('👍'); + expect(getReactForStatus(status)?.get('name')).toEqual('👍'); }); it('returns undefined when a status has no reacts (or favourites)', () => { @@ -173,24 +173,24 @@ describe('getReactForStatus', () => { describe('simulateEmojiReact', () => { it('adds the emoji to the list', () => { const emojiReacts = fromJS([ - { 'count': 2, 'me': false, 'name': '👍' }, - { 'count': 2, 'me': false, 'name': '❤' }, + { 'count': 2, 'me': false, 'name': '👍', 'url': undefined }, + { 'count': 2, 'me': false, 'name': '❤', 'url': undefined }, ]) as ImmutableList>; expect(simulateEmojiReact(emojiReacts, '❤')).toEqual(fromJS([ - { 'count': 2, 'me': false, 'name': '👍' }, - { 'count': 3, 'me': true, 'name': '❤' }, + { 'count': 2, 'me': false, 'name': '👍', 'url': undefined }, + { 'count': 3, 'me': true, 'name': '❤', 'url': undefined }, ])); }); it('creates the emoji if it didn\'t already exist', () => { const emojiReacts = fromJS([ - { 'count': 2, 'me': false, 'name': '👍' }, - { 'count': 2, 'me': false, 'name': '❤' }, + { 'count': 2, 'me': false, 'name': '👍', 'url': undefined }, + { 'count': 2, 'me': false, 'name': '❤', 'url': undefined }, ]) as ImmutableList>; expect(simulateEmojiReact(emojiReacts, '😯')).toEqual(fromJS([ - { 'count': 2, 'me': false, 'name': '👍' }, - { 'count': 2, 'me': false, 'name': '❤' }, - { 'count': 1, 'me': true, 'name': '😯' }, + { 'count': 2, 'me': false, 'name': '👍', 'url': undefined }, + { 'count': 2, 'me': false, 'name': '❤', 'url': undefined }, + { 'count': 1, 'me': true, 'name': '😯', 'url': undefined }, ])); }); }); From 38c99dbc48c4a6ea760a47125297a59d0692bc74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 18 Mar 2023 20:49:13 +0100 Subject: [PATCH 4/4] Add tests from custom reacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../utils/__tests__/emoji-reacts.test.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/soapbox/utils/__tests__/emoji-reacts.test.ts b/app/soapbox/utils/__tests__/emoji-reacts.test.ts index 9b901614c..5d64fdf3d 100644 --- a/app/soapbox/utils/__tests__/emoji-reacts.test.ts +++ b/app/soapbox/utils/__tests__/emoji-reacts.test.ts @@ -193,6 +193,18 @@ describe('simulateEmojiReact', () => { { 'count': 1, 'me': true, 'name': '😯', 'url': undefined }, ])); }); + + it('adds a custom emoji to the list', () => { + const emojiReacts = fromJS([ + { 'count': 2, 'me': false, 'name': '👍', 'url': undefined }, + { 'count': 2, 'me': false, 'name': '❤', 'url': undefined }, + ]) as ImmutableList>; + expect(simulateEmojiReact(emojiReacts, 'soapbox', 'https://gleasonator.com/emoji/Gleasonator/soapbox.png')).toEqual(fromJS([ + { 'count': 2, 'me': false, 'name': '👍', 'url': undefined }, + { 'count': 2, 'me': false, 'name': '❤', 'url': undefined }, + { 'count': 1, 'me': true, 'name': 'soapbox', 'url': 'https://gleasonator.com/emoji/Gleasonator/soapbox.png' }, + ])); + }); }); describe('simulateUnEmojiReact', () => { @@ -218,4 +230,16 @@ describe('simulateUnEmojiReact', () => { { 'count': 2, 'me': false, 'name': '❤' }, ])); }); + + it ('removes custom emoji from the list', () => { + const emojiReacts = fromJS([ + { 'count': 2, 'me': false, 'name': '👍' }, + { 'count': 2, 'me': false, 'name': '❤' }, + { 'count': 1, 'me': true, 'name': 'soapbox', 'url': 'https://gleasonator.com/emoji/Gleasonator/soapbox.png' }, + ]) as ImmutableList>; + expect(simulateUnEmojiReact(emojiReacts, 'soapbox')).toEqual(fromJS([ + { 'count': 2, 'me': false, 'name': '👍' }, + { 'count': 2, 'me': false, 'name': '❤' }, + ])); + }); });