diff --git a/lib/shared/reaction-utils.js b/lib/shared/reaction-utils.js index 8f0d3e705..6e0e17eab 100644 --- a/lib/shared/reaction-utils.js +++ b/lib/shared/reaction-utils.js @@ -1,109 +1,109 @@ // @flow import _sortBy from 'lodash/fp/sortBy.js'; import * as React from 'react'; import { relationshipBlockedInEitherDirection } from './relationship-utils.js'; import { threadHasPermission } from './thread-utils.js'; import { stringForUserExplicit } from './user-utils.js'; import { useENSNames } from '../hooks/ens-cache.js'; import type { ReactionInfo } from '../selectors/chat-selectors.js'; import type { RobotextMessageInfo, ComposableMessageInfo, } from '../types/message-types.js'; import { threadPermissions } from '../types/thread-permission-types.js'; import { type ThreadInfo } from '../types/thread-types.js'; import { useSelector } from '../utils/redux-utils.js'; function useViewerAlreadySelectedMessageReactions( reactions: ReactionInfo, ): $ReadOnlyArray { return React.useMemo(() => { const alreadySelectedEmojis = []; for (const reaction in reactions) { const reactionInfo = reactions[reaction]; if (reactionInfo.viewerReacted) { alreadySelectedEmojis.push(reaction); } } return alreadySelectedEmojis; }, [reactions]); } -type MessageReactionListInfo = { +export type MessageReactionListInfo = { +id: string, +isViewer: boolean, +reaction: string, +username: string, }; function useMessageReactionsList( reactions: ReactionInfo, ): $ReadOnlyArray { const withoutENSNames = React.useMemo(() => { const result = []; for (const reaction in reactions) { const reactionInfo = reactions[reaction]; reactionInfo.users.forEach(user => { result.push({ ...user, username: stringForUserExplicit(user), reaction, }); }); } const sortByNumReactions = (reactionInfo: MessageReactionListInfo) => { const numOfReactions = reactions[reactionInfo.reaction].users.length; return numOfReactions ? -numOfReactions : 0; }; return _sortBy( ([sortByNumReactions, 'username']: $ReadOnlyArray< ((reactionInfo: MessageReactionListInfo) => mixed) | string, >), )(result); }, [reactions]); return useENSNames(withoutENSNames); } function useCanCreateReactionFromMessage( threadInfo: ThreadInfo, targetMessageInfo: ComposableMessageInfo | RobotextMessageInfo, ): boolean { const targetMessageCreatorRelationship = useSelector( state => state.userStore.userInfos[targetMessageInfo.creator.id] ?.relationshipStatus, ); if ( !targetMessageInfo.id || threadInfo.sourceMessageID === targetMessageInfo.id ) { return false; } const creatorRelationshipHasBlock = targetMessageCreatorRelationship && relationshipBlockedInEitherDirection(targetMessageCreatorRelationship); const hasPermission = threadHasPermission( threadInfo, threadPermissions.REACT_TO_MESSAGE, ); return hasPermission && !creatorRelationshipHasBlock; } export { useViewerAlreadySelectedMessageReactions, useMessageReactionsList, useCanCreateReactionFromMessage, }; diff --git a/web/modals/chat/message-reactions-list-item.react.js b/web/modals/chat/message-reactions-list-item.react.js new file mode 100644 index 000000000..4fa843f07 --- /dev/null +++ b/web/modals/chat/message-reactions-list-item.react.js @@ -0,0 +1,37 @@ +// @flow + +import * as React from 'react'; + +import { type MessageReactionListInfo } from 'lib/shared/reaction-utils.js'; + +import css from './message-reactions-modal.css'; +import UserAvatar from '../../avatars/user-avatar.react.js'; + +type Props = { + +messageReactionUser: MessageReactionListInfo, +}; + +function MessageReactionsListItem(props: Props): React.Node { + const { messageReactionUser } = props; + + const messageReactionsListItem = React.useMemo( + () => ( +
+
+ +
{messageReactionUser.username}
+
+
{messageReactionUser.reaction}
+
+ ), + [ + messageReactionUser.id, + messageReactionUser.reaction, + messageReactionUser.username, + ], + ); + + return messageReactionsListItem; +} + +export default MessageReactionsListItem; diff --git a/web/modals/chat/message-reactions-modal.react.js b/web/modals/chat/message-reactions-modal.react.js index 6cff5816e..4ccfb7a88 100644 --- a/web/modals/chat/message-reactions-modal.react.js +++ b/web/modals/chat/message-reactions-modal.react.js @@ -1,43 +1,40 @@ // @flow import * as React from 'react'; import type { ReactionInfo } from 'lib/selectors/chat-selectors.js'; import { useMessageReactionsList } from 'lib/shared/reaction-utils.js'; +import MessageReactionsListItem from './message-reactions-list-item.react.js'; import css from './message-reactions-modal.css'; -import UserAvatar from '../../avatars/user-avatar.react.js'; import Modal from '../modal.react.js'; type Props = { +onClose: () => void, +reactions: ReactionInfo, }; function MessageReactionsModal(props: Props): React.Node { const { onClose, reactions } = props; const messageReactionsList = useMessageReactionsList(reactions); const reactionsList = React.useMemo( () => messageReactionsList.map(messageReactionUser => ( -
-
- -
{messageReactionUser.username}
-
-
{messageReactionUser.reaction}
-
+ )), [messageReactionsList], ); return (
{reactionsList}
); } export default MessageReactionsModal;