diff --git a/web/components/chat/ChatContainer/ChatContainer.tsx b/web/components/chat/ChatContainer/ChatContainer.tsx index 5d6582bad3..789ae924e4 100644 --- a/web/components/chat/ChatContainer/ChatContainer.tsx +++ b/web/components/chat/ChatContainer/ChatContainer.tsx @@ -150,6 +150,7 @@ export const ChatContainer: FC = ({ sentBySelf={message.user?.id === chatUserId} // The local user sent this message sameUserAsLast={shouldCollapseMessages(messages, index)} isAuthorModerator={(message as ChatMessage).user.scopes?.includes('MODERATOR')} + isAuthorBot={(message as ChatMessage).user.scopes?.includes('BOT')} isAuthorAuthenticated={message.user?.authenticated} key={message.id} /> diff --git a/web/components/chat/ChatUserBadge/BotUserBadge.tsx b/web/components/chat/ChatUserBadge/BotUserBadge.tsx new file mode 100644 index 0000000000..b47d67d5f0 --- /dev/null +++ b/web/components/chat/ChatUserBadge/BotUserBadge.tsx @@ -0,0 +1,17 @@ +import dynamic from 'next/dynamic'; +import React, { FC } from 'react'; +import { ChatUserBadge } from './ChatUserBadge'; + +// Lazy loaded components + +const BulbFilled = dynamic(() => import('@ant-design/icons/BulbFilled'), { + ssr: false, +}); + +export type BotBadgeProps = { + userColor: number; +}; + +export const BotUserBadge: FC = ({ userColor }) => ( + } userColor={userColor} title="Bot" /> +); diff --git a/web/components/chat/ChatUserBadge/ChatUserBadge.stories.tsx b/web/components/chat/ChatUserBadge/ChatUserBadge.stories.tsx index d8fad3292d..640c931dda 100644 --- a/web/components/chat/ChatUserBadge/ChatUserBadge.stories.tsx +++ b/web/components/chat/ChatUserBadge/ChatUserBadge.stories.tsx @@ -3,6 +3,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import { ChatUserBadge } from './ChatUserBadge'; import { ModerationBadge } from './ModerationBadge'; import { AuthedUserBadge } from './AuthedUserBadge'; +import { BotUserBadge } from './BotUserBadge'; export default { title: 'owncast/Chat/Messages/User Flag', @@ -24,6 +25,8 @@ const AuthedTemplate: ComponentStory = args => ( ); +const BotTemplate: ComponentStory = args => ; + export const Authenticated = AuthedTemplate.bind({}); Authenticated.args = { userColor: '3', @@ -34,6 +37,11 @@ Moderator.args = { userColor: '5', }; +export const Bot = BotTemplate.bind({}); +Bot.args = { + userColor: '7', +}; + export const Generic = Template.bind({}); Generic.args = { badge: '?', diff --git a/web/components/chat/ChatUserMessage/ChatUserMessage.stories.tsx b/web/components/chat/ChatUserMessage/ChatUserMessage.stories.tsx index de65801203..b00b215d0b 100644 --- a/web/components/chat/ChatUserMessage/ChatUserMessage.stories.tsx +++ b/web/components/chat/ChatUserMessage/ChatUserMessage.stories.tsx @@ -89,6 +89,22 @@ const authenticatedUserMessage: ChatMessage = JSON.parse(`{ }, "body": "I am an authenticated user."}`); +const botUserMessage: ChatMessage = JSON.parse(`{ + "type": "CHAT", + "id": "wY-MEXwnR", + "timestamp": "2022-04-28T20:30:27.001762726Z", + "user": { + "id": "h_5GQ6E7R", + "displayName": "EliteMooseTaskForce", + "displayColor": 7, + "createdAt": "2022-03-24T03:52:37.966584694Z", + "previousNames": ["gifted-nobel", "EliteMooseTaskForce"], + "nameChangedAt": "2022-04-26T23:56:05.531287897Z", + "authenticated": true, + "scopes": ["bot"] + }, + "body": "I am a bot."}`); + export const WithoutModeratorMenu = Template.bind({}); WithoutModeratorMenu.args = { message: standardMessage, @@ -121,6 +137,13 @@ FromAuthenticatedUser.args = { isAuthorAuthenticated: true, }; +export const FromBotUser = Template.bind({}); +FromBotUser.args = { + message: botUserMessage, + showModeratorMenu: false, + isAuthorBot: true, +}; + export const WithStringHighlighted = Template.bind({}); WithStringHighlighted.args = { message: standardMessage, diff --git a/web/components/chat/ChatUserMessage/ChatUserMessage.tsx b/web/components/chat/ChatUserMessage/ChatUserMessage.tsx index 8f0255189a..3f6f58022b 100644 --- a/web/components/chat/ChatUserMessage/ChatUserMessage.tsx +++ b/web/components/chat/ChatUserMessage/ChatUserMessage.tsx @@ -14,6 +14,7 @@ import { accessTokenAtom } from '../../stores/ClientConfigStore'; import { User } from '../../../interfaces/user.model'; import { AuthedUserBadge } from '../ChatUserBadge/AuthedUserBadge'; import { ModerationBadge } from '../ChatUserBadge/ModerationBadge'; +import { BotUserBadge } from '../ChatUserBadge/BotUserBadge'; // Lazy loaded components @@ -35,6 +36,7 @@ export type ChatUserMessageProps = { sameUserAsLast: boolean; isAuthorModerator: boolean; isAuthorAuthenticated: boolean; + isAuthorBot: boolean; }; export type UserTooltipProps = { @@ -61,6 +63,7 @@ export const ChatUserMessage: FC = ({ sameUserAsLast, isAuthorModerator, isAuthorAuthenticated, + isAuthorBot, }) => { const { id: messageId, body, user, timestamp } = message; const { id: userId, displayName, displayColor } = user; @@ -76,7 +79,9 @@ export const ChatUserMessage: FC = ({ if (isAuthorAuthenticated) { badgeNodes.push(); } - + if (isAuthorBot) { + badgeNodes.push(); + } return (