From 39ae17ab7dbe304ccb211699f3e085a826476f1c Mon Sep 17 00:00:00 2001 From: EvilProfesseur Date: Mon, 27 May 2024 16:41:44 +0200 Subject: [PATCH] fix: anonymous convo (#195) Co-authored-by: Borys Juskiw --- .../apps/chat/convo-snippet/convo-snippet.tsx | 16 ++-- .../messaging/conversation/conversation.tsx | 13 +++- .../components/messaging/message/message.tsx | 21 ++++-- .../src/shared/services/messages.service.ts | 74 ++++++++++--------- 4 files changed, 77 insertions(+), 47 deletions(-) diff --git a/frontend/src/apps/chat/convo-snippet/convo-snippet.tsx b/frontend/src/apps/chat/convo-snippet/convo-snippet.tsx index a740b728..e82d6942 100644 --- a/frontend/src/apps/chat/convo-snippet/convo-snippet.tsx +++ b/frontend/src/apps/chat/convo-snippet/convo-snippet.tsx @@ -1,6 +1,8 @@ import { FC, useMemo } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; import classNames from 'classnames'; +import dayjs from 'dayjs'; +import { useIntl } from 'react-intl'; import { Link, useParams } from 'react-router-dom'; import { IConversation } from '../../../models/message'; import { Conversation } from '../../../shared/components/messaging/conversation/conversation'; @@ -12,12 +14,12 @@ import { useMessagesService } from '../../../shared/services/messages.service'; import MemoizedFormattedMessage from 'react-intl/src/components/message'; import './convo-snippet.scss'; -import dayjs from 'dayjs'; export const ConvoSnippet: FC<{ convo: IConversation; delayMultiplier: number; }> = ({ convo, delayMultiplier }) => { + const intl = useIntl(); const { chatId } = useParams(); const { generateAnimation } = useStandardizedAnimation(); const { convoHasUnreadMessages } = useMessagesService(); @@ -44,9 +46,13 @@ export const ConvoSnippet: FC<{ }, [currentUser, convo.participantsAllowedToSendMsgs]); const otherParty = () => { - const others = convo.participants.filter( - (participant) => participant !== currentUser?.handle - ); + const others = convo.participants + .filter((participant) => participant !== currentUser?.handle) + .map((participant) => + convo.anonymizedUsers.includes(participant) + ? intl.formatMessage({ id: 'ANONYMOUS' }) + : participant + ); if (others.length === 1) { return others[0]; @@ -62,7 +68,7 @@ export const ConvoSnippet: FC<{ if (diff === 0) { return date.format('HH:mm:ss'); - } + } if (diff < 3) { return `${now.diff(date, 'hours')} hours ago`; } diff --git a/frontend/src/shared/components/messaging/conversation/conversation.tsx b/frontend/src/shared/components/messaging/conversation/conversation.tsx index d6845daf..a2ccee88 100644 --- a/frontend/src/shared/components/messaging/conversation/conversation.tsx +++ b/frontend/src/shared/components/messaging/conversation/conversation.tsx @@ -7,6 +7,7 @@ import { Message } from '../message/message'; import { setMessagesAsRead } from '../../../../store/messages.slice'; import { IWebsocketContext } from '../../../providers/websocket.model'; import { useWebSocketContext } from '../../../providers/websocket.provider'; +import { useUserService } from '../../../services/user.service'; import './conversation.scss'; @@ -15,6 +16,7 @@ export const Conversation: FC<{ convo: IConversation; className?: string }> = ({ className }) => { const dispatch = useDispatch(); + const { currentUser } = useUserService(); const convoWrapper = useRef(null); const { lastMessage } = useWebSocketContext() as IWebsocketContext; const { chatId, gigId } = useParams(); @@ -47,6 +49,10 @@ export const Conversation: FC<{ convo: IConversation; className?: string }> = ({ [chatId, convo, dispatch, gigId, lastMessage] ); + const senderAnonymized = (sender: string) => + convo.anonymizedUsers.includes(sender) && + currentUser?.handle !== sender; + return ( = ({ }} > {convo.messages.map((msg) => ( - + ))} ); diff --git a/frontend/src/shared/components/messaging/message/message.tsx b/frontend/src/shared/components/messaging/message/message.tsx index b3f07fee..b3fec655 100644 --- a/frontend/src/shared/components/messaging/message/message.tsx +++ b/frontend/src/shared/components/messaging/message/message.tsx @@ -3,33 +3,38 @@ import classNames from 'classnames'; import { IMessage } from '../../../../models/message'; import { useUserService } from '../../../services/user.service'; import { useMessagesService } from '../../../services/messages.service'; +import { useIntl } from 'react-intl'; import './message.scss'; -export const Message: FC<{ message: IMessage; convoId: string }> = ({ - message, - convoId -}) => { +export const Message: FC<{ + message: IMessage; + convoId: string; + senderAnonymized?: boolean; +}> = ({ message, convoId, senderAnonymized = false }) => { + const intl = useIntl(); const { currentUser, isInfluencer } = useUserService(); const { isMessageUnread } = useMessagesService(); const messageClassnames = classNames({ message: true, 'message--own': currentUser?.handle === message.sender, - 'message--unread': isMessageUnread(convoId, message.id), + 'message--unread': isMessageUnread(convoId, message.id) }); const senderClasses = classNames({ - 'message__sender': true, + message__sender: true, 'message__sender--influencer': isInfluencer(message.sender) }); const textClasses = classNames({ - 'message__text': true, + message__text: true, 'message__text--influencer': isInfluencer(message.sender) }); return (

{`${new Date(message.date).toLocaleTimeString()}`} - {`<@${message.sender}>`} + {`<@${senderAnonymized ? intl.formatMessage({ id: 'ANONYMOUS' }) : message.sender}>`} {`${message.text.trim()}`}

); diff --git a/frontend/src/shared/services/messages.service.ts b/frontend/src/shared/services/messages.service.ts index 91b668fb..c8280aab 100644 --- a/frontend/src/shared/services/messages.service.ts +++ b/frontend/src/shared/services/messages.service.ts @@ -29,19 +29,19 @@ export function useMessagesService() { const dispatch = useDispatch(); const intl = useIntl(); const { api } = useApiService(); - const { sendMessage } = - useWebSocketContext() as IWebsocketContext; + const { sendMessage } = useWebSocketContext() as IWebsocketContext; const { displayToast } = useToastService(); const { currentUser } = useUserService(); const [fetchingConvo, setFetchingConvo] = useState(false); const gigConversations = useSelector(selectGigConversations); const unreadMessages = useSelector(selectUnreadMessages); - const unreadGigMessages = useSelector(selectUnreadGigMessages) + const unreadGigMessages = useSelector(selectUnreadGigMessages); - const createMessage: (text: string, senderHandle?: string) => IMessage = ( - text, - senderHandle - ) => { + const createMessage: ( + text: string, + senderHandle?: string, + anonymize?: boolean + ) => IMessage = (text, senderHandle) => { return { id: uuidv4(), date: dayjs().add(100, 'year').toISOString(), @@ -55,29 +55,30 @@ export function useMessagesService() { id?: string, anonymize?: boolean ) => Promise = (participants, id, anonymize) => - new Promise((resolve, reject) => { - const convoId = id ?? uuidv4(); - const convo: IConversation = { - id: convoId, - anonymizedUsers: anonymize ? [currentUser!.handle] : [], - gigConversation: false, - participants, - messages: [...createInitialMessages(participants)] - }; - - api.url('Conversation') - .post(convo) - .json() - .catch(() => reject()) - .then((conversation) => { - if (!conversation) { - reject(); - return; - } - dispatch(addConversation(conversation)); - resolve(conversation.id); - }); - }); + new Promise((resolve, reject) => { + const convoId = id ?? uuidv4(); + const anonymizedUsers = anonymize ? [currentUser!.handle] : []; + const convo: IConversation = { + id: convoId, + anonymizedUsers, + gigConversation: false, + participants, + messages: [...createInitialMessages(participants, anonymize)] + }; + + api.url('Conversation') + .post(convo) + .json() + .catch(() => reject()) + .then((conversation) => { + if (!conversation) { + reject(); + return; + } + dispatch(addConversation(conversation)); + resolve(conversation.id); + }); + }); const getGigConvo = async (gigId: string): Promise => { return await api @@ -85,11 +86,18 @@ export function useMessagesService() { .json(); }; - const createInitialMessages = (participants: string[]): IMessage[] => { + const createInitialMessages = ( + participants: string[], + anonymize?: boolean + ): IMessage[] => { const initialMessages: IMessage[] = []; participants.forEach((participant) => { + const handle = + anonymize && participant === currentUser?.handle + ? intl.formatMessage({ id: 'ANONYMOUS' }) + : participant; const message = createMessage( - `<@${participant} ${intl.formatMessage({ + `<@${handle} ${intl.formatMessage({ id: 'ENTERED_THE_CHAT' })}>`, participant @@ -161,7 +169,7 @@ export function useMessagesService() { const gigConvoHasUnreadMessages = (convoId: string): boolean => { return unreadGigMessages[convoId]?.length > 0; - } + }; const updateConversationHashes = ( hashes: Record,