Handle hide/show chat messages via moderation. Closes #1986

This commit is contained in:
Gabe Kangas 2022-09-04 17:58:06 -07:00
parent c0dc2eb707
commit ac7e095fdf
No known key found for this signature in database
GPG key ID: 9A56337728BC81EA
5 changed files with 42 additions and 4 deletions

View file

@ -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<string>(accessTokenAtom);
const color = `var(--theme-color-users-${displayColor})`;
const formattedTimestamp = `Sent ${formatTimestamp(timestamp)}`;
@ -81,7 +84,7 @@ export default function ChatUserMessage({
<div className={s.modMenuWrapper}>
<ChatModerationActionMenu
messageID={messageId}
accessToken=""
accessToken={accessToken}
userID={userId}
userDisplayName={displayName}
/>

View file

@ -17,6 +17,7 @@ import {
ConnectedClientInfoEvent,
MessageType,
ChatEvent,
MessageVisibilityEvent,
SocketEvent,
} from '../../interfaces/socket-events';
@ -111,6 +112,11 @@ export const clockSkewAtom = atom<Number>({
default: 0.0,
});
export const removedMessageIdsAtom = atom<string[]>({
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<ChatMessage[]>({
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<AppStateOptions>(appStateAtom);
const setGlobalFatalErrorMessage = useSetRecoilState<DisplayableError>(fatalErrorStateAtom);
const setWebsocketService = useSetRecoilState<WebsocketService>(websocketServiceAtom);
const [hiddenMessageIds, setHiddenMessageIds] = useRecoilState<string[]>(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);
}

View file

@ -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<ChatMessage[]>(chatMessagesAtom);
const chatDisplayName = useRecoilValue<string>(chatDisplayNameAtom);
const chatUserId = useRecoilValue<string>(chatUserIdAtom);
const isChatModerator = useRecoilValue<boolean>(isChatModeratorAtom);
const messages = useRecoilValue<ChatMessage[]>(visibleChatMessagesSelector);
return (
<Sider className={s.root} collapsedWidth={0} width={320}>

View file

@ -37,3 +37,8 @@ export interface NameChangeEvent extends SocketEvent {
user: User;
oldName: string;
}
export interface MessageVisibilityEvent extends SocketEvent {
visible: boolean;
ids: string[];
}

View file

@ -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`;