From 9a2a43d91663e0e9659cdfdf9cef54f97ae7c663 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Thu, 14 Jul 2022 20:36:47 -0700 Subject: [PATCH] Add moderator status chat message. Closes #1999 --- .../chat/ChatContainer/ChatContainer.tsx | 36 +++++++++++++++++-- .../ChatModeratorNotification.module.scss | 19 ++++++++++ .../ChatModeratorNotification.tsx | 12 +++++++ web/components/stores/ClientConfigStore.tsx | 1 + .../ChatModeratorNotification.stories.tsx | 17 +++++++++ web/styles/mixins.scss | 5 +++ 6 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 web/components/chat/ChatModeratorNotification/ChatModeratorNotification.module.scss create mode 100644 web/components/chat/ChatModeratorNotification/ChatModeratorNotification.tsx create mode 100644 web/stories/ChatModeratorNotification.stories.tsx create mode 100644 web/styles/mixins.scss diff --git a/web/components/chat/ChatContainer/ChatContainer.tsx b/web/components/chat/ChatContainer/ChatContainer.tsx index 1f9c9940c..30c246340 100644 --- a/web/components/chat/ChatContainer/ChatContainer.tsx +++ b/web/components/chat/ChatContainer/ChatContainer.tsx @@ -2,10 +2,15 @@ import { Button } from 'antd'; import { Virtuoso } from 'react-virtuoso'; import { useState, useMemo, useRef } from 'react'; import { EditFilled, VerticalAlignBottomOutlined } from '@ant-design/icons'; -import { MessageType, NameChangeEvent } from '../../../interfaces/socket-events'; +import { + ConnectedClientInfoEvent, + MessageType, + NameChangeEvent, +} from '../../../interfaces/socket-events'; import s from './ChatContainer.module.scss'; import { ChatMessage } from '../../../interfaces/chat-message.model'; import { ChatTextField, ChatUserMessage } from '..'; +import ChatModeratorNotification from '../ChatModeratorNotification/ChatModeratorNotification'; interface Props { messages: ChatMessage[]; @@ -43,7 +48,20 @@ export default function ChatContainer(props: Props) { ); }; - const getViewForMessage = (index: number, message: ChatMessage | NameChangeEvent) => { + const getConnectedInfoMessage = (message: ConnectedClientInfoEvent) => { + const modStatusUpdate = checkIsModerator(message); + if (!modStatusUpdate) { + return null; + } + + // Alert the user that they are a moderator. + return ; + }; + + const getViewForMessage = ( + index: number, + message: ChatMessage | NameChangeEvent | ConnectedClientInfoEvent, + ) => { switch (message.type) { case MessageType.CHAT: return ( @@ -58,6 +76,9 @@ export default function ChatContainer(props: Props) { ); case MessageType.NAME_CHANGE: return getNameChangeViewForMessage(message as NameChangeEvent); + case MessageType.CONNECTED_USER_INFO: + return getConnectedInfoMessage(message); + default: return null; } @@ -120,3 +141,14 @@ function isSameUserAsLast(messages: ChatMessage[], index: number) { return id === lastMessage?.user.id; } + +function checkIsModerator(message) { + const { user } = message; + const { scopes } = user; + + if (!scopes || scopes.length === 0) { + return false; + } + + return scopes.includes('MODERATOR'); +} diff --git a/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.module.scss b/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.module.scss new file mode 100644 index 000000000..a235f1f6c --- /dev/null +++ b/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.module.scss @@ -0,0 +1,19 @@ +@import 'styles/mixins.scss'; + +.chatModerationNotification { + background-color: var(--theme-background-primary); + margin: 5px; + border-radius: 15px; + border-color: rgba(0, 0, 0, 0.3); + border-width: 1px; + border-style: solid; + padding: 10px 10px; + max-width: 400px; + @include flexCenter; + + .icon { + margin-right: 10px; + width: 20px; + height: 20px; + } +} diff --git a/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.tsx b/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.tsx new file mode 100644 index 000000000..224e33e78 --- /dev/null +++ b/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.tsx @@ -0,0 +1,12 @@ +import s from './ChatModeratorNotification.module.scss'; +import Icon from '../../../assets/images/moderator.svg'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export default function ModeratorNotification() { + return ( +
+ + You are now a moderator. +
+ ); +} diff --git a/web/components/stores/ClientConfigStore.tsx b/web/components/stores/ClientConfigStore.tsx index 0b70e6f04..0c7220b81 100644 --- a/web/components/stores/ClientConfigStore.tsx +++ b/web/components/stores/ClientConfigStore.tsx @@ -258,6 +258,7 @@ export function ClientConfigStore() { setChatUserId, setIsChatModerator, ); + setChatMessages(currentState => [...currentState, message as ChatEvent]); break; case MessageType.CHAT: setChatMessages(currentState => [...currentState, message as ChatEvent]); diff --git a/web/stories/ChatModeratorNotification.stories.tsx b/web/stories/ChatModeratorNotification.stories.tsx new file mode 100644 index 000000000..1ecf35500 --- /dev/null +++ b/web/stories/ChatModeratorNotification.stories.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import ChatModeratorNotification from '../components/chat/ChatModeratorNotification/ChatModeratorNotification'; + +export default { + title: 'owncast/Chat/Messages/Moderation Role Notification', + component: ChatModeratorNotification, + parameters: {}, +} as ComponentMeta; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const Template: ComponentStory = args => ( + +); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const Basic = Template.bind({}); diff --git a/web/styles/mixins.scss b/web/styles/mixins.scss new file mode 100644 index 000000000..d7ff1da49 --- /dev/null +++ b/web/styles/mixins.scss @@ -0,0 +1,5 @@ +@mixin flexCenter { + display: flex; + justify-content: center; + align-items: center; +}