From ac7e095fdf93dcc5773e52e0fe24f8e09099f7c7 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sun, 4 Sep 2022 17:58:06 -0700 Subject: [PATCH] Handle hide/show chat messages via moderation. Closes #1986 --- .../chat/ChatUserMessage/ChatUserMessage.tsx | 5 +++- web/components/stores/ClientConfigStore.tsx | 30 +++++++++++++++++++ web/components/ui/Sidebar/Sidebar.tsx | 4 +-- web/interfaces/socket-events.ts | 5 ++++ web/utils/apis.ts | 2 +- 5 files changed, 42 insertions(+), 4 deletions(-) diff --git a/web/components/chat/ChatUserMessage/ChatUserMessage.tsx b/web/components/chat/ChatUserMessage/ChatUserMessage.tsx index 938c00680..56376b10f 100644 --- a/web/components/chat/ChatUserMessage/ChatUserMessage.tsx +++ b/web/components/chat/ChatUserMessage/ChatUserMessage.tsx @@ -5,11 +5,13 @@ import he from 'he'; import cn from 'classnames'; import { Tooltip } from 'antd'; import { LinkOutlined } from '@ant-design/icons'; +import { useRecoilValue } from 'recoil'; import s from './ChatUserMessage.module.scss'; import { formatTimestamp } from './messageFmt'; import { ChatMessage } from '../../../interfaces/chat-message.model'; import ChatModerationActionMenu from '../ChatModerationActionMenu/ChatModerationActionMenu'; import ChatUserBadge from '../ChatUserBadge/ChatUserBadge'; +import { accessTokenAtom } from '../../stores/ClientConfigStore'; interface Props { message: ChatMessage; @@ -32,6 +34,7 @@ export default function ChatUserMessage({ }: Props) { const { id: messageId, body, user, timestamp } = message; const { id: userId, displayName, displayColor } = user; + const accessToken = useRecoilValue(accessTokenAtom); const color = `var(--theme-color-users-${displayColor})`; const formattedTimestamp = `Sent ${formatTimestamp(timestamp)}`; @@ -81,7 +84,7 @@ export default function ChatUserMessage({
diff --git a/web/components/stores/ClientConfigStore.tsx b/web/components/stores/ClientConfigStore.tsx index 695c0b389..6d777c251 100644 --- a/web/components/stores/ClientConfigStore.tsx +++ b/web/components/stores/ClientConfigStore.tsx @@ -17,6 +17,7 @@ import { ConnectedClientInfoEvent, MessageType, ChatEvent, + MessageVisibilityEvent, SocketEvent, } from '../../interfaces/socket-events'; @@ -111,6 +112,11 @@ export const clockSkewAtom = atom({ default: 0.0, }); +export const removedMessageIdsAtom = atom({ + key: 'removedMessageIds', + default: [], +}); + // Chat is visible if the user wishes it to be visible AND the required // chat state is set. export const isChatVisibleSelector = selector({ @@ -144,6 +150,15 @@ export const isOnlineSelector = selector({ }, }); +export const visibleChatMessagesSelector = selector({ + key: 'visibleChatMessagesSelector', + get: ({ get }) => { + const messages: ChatMessage[] = get(chatMessagesAtom); + const removedIds: string[] = get(removedMessageIdsAtom); + return messages.filter(message => !removedIds.includes(message.id)); + }, +}); + // Take a nested object of state metadata and merge it into // a single flattened node. function mergeMeta(meta) { @@ -171,6 +186,7 @@ export function ClientConfigStore() { const setAppState = useSetRecoilState(appStateAtom); const setGlobalFatalErrorMessage = useSetRecoilState(fatalErrorStateAtom); const setWebsocketService = useSetRecoilState(websocketServiceAtom); + const [hiddenMessageIds, setHiddenMessageIds] = useRecoilState(removedMessageIdsAtom); let ws: WebsocketService; @@ -259,6 +275,17 @@ export function ClientConfigStore() { handleUserRegistration(); }; + const handleMessageVisibilityChange = (message: MessageVisibilityEvent) => { + const { ids, visible } = message; + if (visible) { + const updatedIds = hiddenMessageIds.filter(id => !ids.includes(id)); + setHiddenMessageIds(updatedIds); + } else { + const updatedIds = [...hiddenMessageIds, ...ids]; + setHiddenMessageIds(updatedIds); + } + }; + const handleMessage = (message: SocketEvent) => { switch (message.type) { case MessageType.ERROR_NEEDS_REGISTRATION: @@ -287,6 +314,9 @@ export function ClientConfigStore() { case MessageType.SYSTEM: setChatMessages(currentState => [...currentState, message as ChatEvent]); break; + case MessageType.VISIBILITY_UPDATE: + handleMessageVisibilityChange(message as MessageVisibilityEvent); + break; default: console.error('Unknown socket message type: ', message.type); } diff --git a/web/components/ui/Sidebar/Sidebar.tsx b/web/components/ui/Sidebar/Sidebar.tsx index 0cc081a18..e4f66307e 100644 --- a/web/components/ui/Sidebar/Sidebar.tsx +++ b/web/components/ui/Sidebar/Sidebar.tsx @@ -5,17 +5,17 @@ import { ChatContainer } from '../../chat'; import s from './Sidebar.module.scss'; import { - chatMessagesAtom, chatDisplayNameAtom, chatUserIdAtom, isChatModeratorAtom, + visibleChatMessagesSelector, } from '../../stores/ClientConfigStore'; export default function Sidebar() { - const messages = useRecoilValue(chatMessagesAtom); const chatDisplayName = useRecoilValue(chatDisplayNameAtom); const chatUserId = useRecoilValue(chatUserIdAtom); const isChatModerator = useRecoilValue(isChatModeratorAtom); + const messages = useRecoilValue(visibleChatMessagesSelector); return ( diff --git a/web/interfaces/socket-events.ts b/web/interfaces/socket-events.ts index 1672ae712..783bb88c8 100644 --- a/web/interfaces/socket-events.ts +++ b/web/interfaces/socket-events.ts @@ -37,3 +37,8 @@ export interface NameChangeEvent extends SocketEvent { user: User; oldName: string; } + +export interface MessageVisibilityEvent extends SocketEvent { + visible: boolean; + ids: string[]; +} diff --git a/web/utils/apis.ts b/web/utils/apis.ts index 04a320423..a6e579721 100644 --- a/web/utils/apis.ts +++ b/web/utils/apis.ts @@ -62,7 +62,7 @@ export const LOGS_WARN = `${API_LOCATION}logs/warnings`; export const CHAT_HISTORY = `${API_LOCATION}chat/messages`; // Get chat history -export const UPDATE_CHAT_MESSGAE_VIZ = `${NEXT_PUBLIC_API_HOST}api/chat/messagevisibility`; +export const UPDATE_CHAT_MESSGAE_VIZ = `/api/admin/chat/messagevisibility`; // Get all access tokens export const ACCESS_TOKENS = `${API_LOCATION}accesstokens`;