diff --git a/lib/selectors/chat-selectors.js b/lib/selectors/chat-selectors.js --- a/lib/selectors/chat-selectors.js +++ b/lib/selectors/chat-selectors.js @@ -277,7 +277,7 @@ endsCluster: boolean, +robotext: EntityText, +threadCreatedFromMessage: ?ThreadInfo, - +reactions: $ReadOnlyMap, + +reactions: ReactionInfo, }; export type ChatMessageInfoItem = | RobotextChatMessageInfoItem @@ -290,9 +290,11 @@ +startsCluster: boolean, endsCluster: boolean, +threadCreatedFromMessage: ?ThreadInfo, - +reactions: $ReadOnlyMap, + +reactions: ReactionInfo, }; export type ChatMessageItem = { itemType: 'loader' } | ChatMessageInfoItem; + +export type ReactionInfo = { [reaction: string]: MessageReactionInfo }; export type MessageReactionInfo = { +viewerReacted: boolean, +users: $ReadOnlyArray, @@ -465,7 +467,7 @@ startsCluster, endsCluster: false, threadCreatedFromMessage, - reactions: renderedReactions, + reactions: Object.fromEntries(renderedReactions), }); } else { invariant( @@ -487,7 +489,7 @@ endsCluster: false, threadCreatedFromMessage, robotext, - reactions: renderedReactions, + reactions: Object.fromEntries(renderedReactions), }); } lastMessageInfo = originalMessageInfo; diff --git a/lib/shared/reaction-utils.js b/lib/shared/reaction-utils.js --- a/lib/shared/reaction-utils.js +++ b/lib/shared/reaction-utils.js @@ -1,6 +1,5 @@ // @flow -import invariant from 'invariant'; import _sortBy from 'lodash/fp/sortBy.js'; import * as React from 'react'; @@ -8,7 +7,7 @@ import { threadHasPermission } from './thread-utils.js'; import { stringForUserExplicit } from './user-utils.js'; import { useENSNames } from '../hooks/ens-cache.js'; -import type { MessageReactionInfo } from '../selectors/chat-selectors.js'; +import type { ReactionInfo } from '../selectors/chat-selectors.js'; import type { RobotextMessageInfo, ComposableMessageInfo, @@ -16,14 +15,11 @@ import { threadPermissions, type ThreadInfo } from '../types/thread-types.js'; import { useSelector } from '../utils/redux-utils.js'; -function stringForReactionList( - reactions: $ReadOnlyMap, -): string { +function stringForReactionList(reactions: ReactionInfo): string { const reactionText = []; - for (const reaction of reactions.keys()) { - const reactionInfo = reactions.get(reaction); - invariant(reactionInfo, 'reactionInfo should be set'); + for (const reaction in reactions) { + const reactionInfo = reactions[reaction]; reactionText.push(reaction); const { length: numberOfReacts } = reactionInfo.users; @@ -44,12 +40,14 @@ }; function useMessageReactionsList( - reactions: $ReadOnlyMap, + reactions: ReactionInfo, ): $ReadOnlyArray { const withoutENSNames = React.useMemo(() => { const result = []; - for (const [reaction, reactionInfo] of reactions) { + for (const reaction in reactions) { + const reactionInfo = reactions[reaction]; + reactionInfo.users.forEach(user => { result.push({ ...user, @@ -60,7 +58,7 @@ } const sortByNumReactions = (reactionInfo: MessageReactionListInfo) => { - const numOfReactions = reactions.get(reactionInfo.reaction)?.users.length; + const numOfReactions = reactions[reactionInfo.reaction].users.length; return numOfReactions ? -numOfReactions : 0; }; diff --git a/lib/shared/reaction-utils.test.js b/lib/shared/reaction-utils.test.js --- a/lib/shared/reaction-utils.test.js +++ b/lib/shared/reaction-utils.test.js @@ -24,7 +24,9 @@ const reactionsMap = new Map(); reactionsMap.set('👍', messageLikesInfo); - expect(stringForReactionList(reactionsMap)).toBe('👍 3'); + const reactions = Object.fromEntries(reactionsMap); + + expect(stringForReactionList(reactions)).toBe('👍 3'); }, ); @@ -45,7 +47,9 @@ const reactionsMap = new Map(); reactionsMap.set('👍', messageLikesInfo); - expect(stringForReactionList(reactionsMap)).toBe('👍 3'); + const reactions = Object.fromEntries(reactionsMap); + + expect(stringForReactionList(reactions)).toBe('👍 3'); }, ); @@ -64,7 +68,9 @@ const reactionsMap = new Map(); reactionsMap.set('👍', messageLikesInfo); - expect(stringForReactionList(reactionsMap)).toBe('👍'); + const reactions = Object.fromEntries(reactionsMap); + + expect(stringForReactionList(reactions)).toBe('👍'); }, ); @@ -83,14 +89,18 @@ const reactionsMap = new Map(); reactionsMap.set('👍', messageLikesInfo); - expect(stringForReactionList(reactionsMap)).toBe('👍'); + const reactions = Object.fromEntries(reactionsMap); + + expect(stringForReactionList(reactions)).toBe('👍'); }, ); it('should return an empty string for a message no reactions', () => { const reactionsMap = new Map(); - expect(stringForReactionList(reactionsMap)).toBe(''); + const reactions = Object.fromEntries(reactionsMap); + + expect(stringForReactionList(reactions)).toBe(''); }); it( @@ -119,7 +129,9 @@ reactionsMap.set('👍', messageLikesInfo); reactionsMap.set('😆', messageLaughsInfo); - expect(stringForReactionList(reactionsMap)).toBe('👍 😆 3'); + const reactions = Object.fromEntries(reactionsMap); + + expect(stringForReactionList(reactions)).toBe('👍 😆 3'); }, ); @@ -149,7 +161,9 @@ const reactionsMap = new Map(); reactionsMap.set('👍', messageLikesInfo); - expect(stringForReactionList(reactionsMap)).toBe('👍 9+'); + const reactions = Object.fromEntries(reactionsMap); + + expect(stringForReactionList(reactions)).toBe('👍 9+'); }, ); }, diff --git a/native/chat/composed-message.react.js b/native/chat/composed-message.react.js --- a/native/chat/composed-message.react.js +++ b/native/chat/composed-message.react.js @@ -138,7 +138,10 @@ ); let inlineEngagement = null; - if (item.threadCreatedFromMessage || item.reactions.size > 0) { + if ( + item.threadCreatedFromMessage || + Object.keys(item.reactions).length > 0 + ) { const positioning = isViewer ? 'right' : 'left'; const inlineEngagementPositionStyle = positioning === 'left' diff --git a/native/chat/inline-engagement.react.js b/native/chat/inline-engagement.react.js --- a/native/chat/inline-engagement.react.js +++ b/native/chat/inline-engagement.react.js @@ -9,7 +9,7 @@ } from 'react-native-reanimated'; import useInlineEngagementText from 'lib/hooks/inline-engagement-text.react.js'; -import type { MessageReactionInfo } from 'lib/selectors/chat-selectors.js'; +import type { ReactionInfo } from 'lib/selectors/chat-selectors.js'; import { stringForReactionList } from 'lib/shared/reaction-utils.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; @@ -28,7 +28,7 @@ type Props = { +threadInfo: ?ThreadInfo, - +reactions?: $ReadOnlyMap, + +reactions?: ReactionInfo, +disabled?: boolean, }; function InlineEngagement(props: Props): React.Node { @@ -88,7 +88,7 @@ ); const reactionList = React.useMemo(() => { - if (!reactions || reactions.size === 0) { + if (!reactions || Object.keys(reactions).length === 0) { return null; } diff --git a/native/chat/message-reactions-modal.react.js b/native/chat/message-reactions-modal.react.js --- a/native/chat/message-reactions-modal.react.js +++ b/native/chat/message-reactions-modal.react.js @@ -6,7 +6,7 @@ import { View, Text, FlatList, TouchableHighlight } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; -import type { MessageReactionInfo } from 'lib/selectors/chat-selectors.js'; +import type { ReactionInfo } from 'lib/selectors/chat-selectors.js'; import { useMessageReactionsList } from 'lib/shared/reaction-utils.js'; import Modal from '../components/modal.react.js'; @@ -15,7 +15,7 @@ import { useColors, useStyles } from '../themes/colors.js'; export type MessageReactionsModalParams = { - +reactions: $ReadOnlyMap, + +reactions: ReactionInfo, }; type Props = { diff --git a/native/chat/multimedia-message-utils.js b/native/chat/multimedia-message-utils.js --- a/native/chat/multimedia-message-utils.js +++ b/native/chat/multimedia-message-utils.js @@ -117,7 +117,7 @@ if (multimediaMessageSendFailed(item)) { height += failedSendHeight; } - if (item.threadCreatedFromMessage || item.reactions.size > 0) { + if (item.threadCreatedFromMessage || Object.keys(item.reactions).length > 0) { height += inlineEngagementStyle.height + inlineEngagementStyle.marginTop + diff --git a/native/chat/reaction-message-utils.js b/native/chat/reaction-message-utils.js --- a/native/chat/reaction-message-utils.js +++ b/native/chat/reaction-message-utils.js @@ -8,7 +8,7 @@ sendReactionMessage, sendReactionMessageActionTypes, } from 'lib/actions/message-actions.js'; -import type { MessageReactionInfo } from 'lib/selectors/chat-selectors.js'; +import type { ReactionInfo } from 'lib/selectors/chat-selectors.js'; import { messageTypes } from 'lib/types/message-types.js'; import type { RawReactionMessageInfo } from 'lib/types/messages/reaction.js'; import { @@ -28,7 +28,7 @@ messageID: ?string, localID: string, threadID: string, - reactions: $ReadOnlyMap, + reactions: ReactionInfo, ): (reaction: string) => mixed { const viewerID = useSelector( state => state.currentUserInfo && state.currentUserInfo.id, @@ -45,7 +45,10 @@ invariant(viewerID, 'viewerID should be set'); - const viewerReacted = reactions.get(reaction)?.viewerReacted; + let viewerReacted = false; + if (Object.keys(reactions).length > 0) { + viewerReacted = reactions[reaction].viewerReacted; + } const action = viewerReacted ? 'remove_reaction' : 'add_reaction'; const reactionMessagePromise = (async () => { diff --git a/native/chat/robotext-message.react.js b/native/chat/robotext-message.react.js --- a/native/chat/robotext-message.react.js +++ b/native/chat/robotext-message.react.js @@ -53,7 +53,7 @@ const styles = useStyles(unboundStyles); let inlineEngagement = null; - if (item.threadCreatedFromMessage || item.reactions.size > 0) { + if (item.threadCreatedFromMessage || Object.keys(item.reactions).length > 0) { inlineEngagement = ( 0) { + if (item.threadCreatedFromMessage || Object.keys(item.reactions).length > 0) { height += inlineEngagementStyle.height + inlineEngagementStyle.marginTop + @@ -79,7 +79,7 @@ function robotextMessageItemHeight( item: ChatRobotextMessageInfoItemWithHeight, ): number { - if (item.threadCreatedFromMessage || item.reactions.size > 0) { + if (item.threadCreatedFromMessage || Object.keys(item.reactions).length > 0) { return item.contentHeight + inlineEngagementStyle.height; } return item.contentHeight; diff --git a/native/types/chat-types.js b/native/types/chat-types.js --- a/native/types/chat-types.js +++ b/native/types/chat-types.js @@ -1,6 +1,6 @@ // @flow -import type { MessageReactionInfo } from 'lib/selectors/chat-selectors.js'; +import type { ReactionInfo } from 'lib/selectors/chat-selectors.js'; import type { LocalMessageInfo, MultimediaMessageInfo, @@ -23,7 +23,7 @@ +robotext: EntityText, +threadCreatedFromMessage: ?ThreadInfo, +contentHeight: number, - +reactions: $ReadOnlyMap, + +reactions: ReactionInfo, }; export type ChatTextMessageInfoItemWithHeight = { @@ -37,7 +37,7 @@ +endsCluster: boolean, +contentHeight: number, +threadCreatedFromMessage: ?ThreadInfo, - +reactions: $ReadOnlyMap, + +reactions: ReactionInfo, }; export type MultimediaContentSizes = { @@ -57,7 +57,7 @@ +endsCluster: boolean, +threadCreatedFromMessage: ?ThreadInfo, +pendingUploads: ?MessagePendingUploads, - +reactions: $ReadOnlyMap, + +reactions: ReactionInfo, }; export type ChatMessageInfoItemWithHeight = diff --git a/web/chat/composed-message.react.js b/web/chat/composed-message.react.js --- a/web/chat/composed-message.react.js +++ b/web/chat/composed-message.react.js @@ -123,7 +123,7 @@ let inlineEngagement = null; if ( (this.props.containsInlineEngagement && item.threadCreatedFromMessage) || - item.reactions.size > 0 + Object.keys(item.reactions).length > 0 ) { const positioning = isViewer ? 'right' : 'left'; inlineEngagement = ( diff --git a/web/chat/inline-engagement.react.js b/web/chat/inline-engagement.react.js --- a/web/chat/inline-engagement.react.js +++ b/web/chat/inline-engagement.react.js @@ -5,7 +5,7 @@ import { useModalContext } from 'lib/components/modal-provider.react.js'; import useInlineEngagementText from 'lib/hooks/inline-engagement-text.react.js'; -import type { MessageReactionInfo } from 'lib/selectors/chat-selectors.js'; +import type { ReactionInfo } from 'lib/selectors/chat-selectors.js'; import { stringForReactionList } from 'lib/shared/reaction-utils.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; @@ -16,7 +16,7 @@ type Props = { +threadInfo: ?ThreadInfo, - +reactions?: $ReadOnlyMap, + +reactions?: ReactionInfo, +positioning: 'left' | 'center' | 'right', }; function InlineEngagement(props: Props): React.Node { @@ -33,7 +33,7 @@ }, ]); - const reactionsExist = reactions && reactions.size > 0; + const reactionsExist = reactions && Object.keys(reactions).length > 0; const threadsContainerClasses = classNames({ [css.threadsContainer]: threadInfo && !reactionsExist, @@ -74,7 +74,7 @@ ); const reactionsList = React.useMemo(() => { - if (!reactions || reactions.size === 0) { + if (!reactions || Object.keys(reactions).length === 0) { return null; } diff --git a/web/chat/message-tooltip.react.js b/web/chat/message-tooltip.react.js --- a/web/chat/message-tooltip.react.js +++ b/web/chat/message-tooltip.react.js @@ -121,7 +121,10 @@ const onEmojiSelect = React.useCallback( emoji => { const reactionInput = emoji.native; - const viewerReacted = !!reactions.get(reactionInput)?.viewerReacted; + let viewerReacted = false; + if (Object.keys(reactions).length > 0) { + viewerReacted = reactions[reactionInput].viewerReacted; + } const action = viewerReacted ? 'remove_reaction' : 'add_reaction'; sendReaction(reactionInput, action); diff --git a/web/chat/robotext-message.react.js b/web/chat/robotext-message.react.js --- a/web/chat/robotext-message.react.js +++ b/web/chat/robotext-message.react.js @@ -38,7 +38,7 @@ let inlineEngagement; const { item } = props; const { threadCreatedFromMessage, reactions } = item; - if (threadCreatedFromMessage || reactions.size > 0) { + if (threadCreatedFromMessage || Object.keys(reactions).length > 0) { inlineEngagement = (
void, - +reactions: $ReadOnlyMap, + +reactions: ReactionInfo, }; function MessageReactionsModal(props: Props): React.Node {