mirror of
https://github.com/owncast/owncast.git
synced 2025-11-02 20:23:29 +08:00
@ -14,6 +14,7 @@ import { ChatTextField } from '../ChatTextField/ChatTextField';
|
||||
import { ChatModeratorNotification } from '../ChatModeratorNotification/ChatModeratorNotification';
|
||||
import { ChatSystemMessage } from '../ChatSystemMessage/ChatSystemMessage';
|
||||
import { ChatJoinMessage } from '../ChatJoinMessage/ChatJoinMessage';
|
||||
import { ChatPartMessage } from '../ChatPartMessage/ChatPartMessage';
|
||||
import { ScrollToBotBtn } from './ScrollToBotBtn';
|
||||
import { ChatActionMessage } from '../ChatActionMessage/ChatActionMessage';
|
||||
import { ChatSocialMessage } from '../ChatSocialMessage/ChatSocialMessage';
|
||||
@ -137,6 +138,20 @@ export const ChatContainer: FC<ChatContainerProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const getUserPartMessage = (message: ChatMessage) => {
|
||||
const {
|
||||
user: { displayName, displayColor },
|
||||
} = message;
|
||||
const isAuthorModerator = checkIsModerator(message);
|
||||
return (
|
||||
<ChatPartMessage
|
||||
displayName={displayName}
|
||||
userColor={displayColor}
|
||||
isAuthorModerator={isAuthorModerator}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const getActionMessage = (message: ChatMessage) => {
|
||||
const { body } = message;
|
||||
return <ChatActionMessage body={body} />;
|
||||
@ -185,6 +200,8 @@ export const ChatContainer: FC<ChatContainerProps> = ({
|
||||
return getConnectedInfoMessage(message as ConnectedClientInfoEvent);
|
||||
case MessageType.USER_JOINED:
|
||||
return getUserJoinedMessage(message as ChatMessage);
|
||||
case MessageType.USER_PARTED:
|
||||
return getUserPartMessage(message as ChatMessage);
|
||||
case MessageType.CHAT_ACTION:
|
||||
return getActionMessage(message as ChatMessage);
|
||||
case MessageType.SYSTEM:
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
.root {
|
||||
display: inline-flex;
|
||||
padding: 10px 0;
|
||||
color: var(--theme-color-components-chat-text);
|
||||
font-weight: 400;
|
||||
font-size: var(--chat-message-text-size);
|
||||
|
||||
.moderatorBadge,
|
||||
.user {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding: 0 var(--chat-notification-icon-padding) 0 16px;
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { ChatPartMessage } from './ChatPartMessage';
|
||||
import Mock from '../../../stories/assets/mocks/chatmessage-action.png';
|
||||
|
||||
export default {
|
||||
title: 'owncast/Chat/Messages/Chat Part',
|
||||
component: ChatPartMessage,
|
||||
argTypes: {
|
||||
userColor: {
|
||||
options: ['0', '1', '2', '3', '4', '5', '6', '7'],
|
||||
control: { type: 'select' },
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'image',
|
||||
url: Mock,
|
||||
},
|
||||
docs: {
|
||||
description: {
|
||||
component: `This is shown when a chat participant parts.`,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof ChatPartMessage>;
|
||||
|
||||
const Template: ComponentStory<typeof ChatPartMessage> = args => <ChatPartMessage {...args} />;
|
||||
|
||||
export const Regular = Template.bind({});
|
||||
Regular.args = {
|
||||
displayName: 'RandomChatter',
|
||||
isAuthorModerator: false,
|
||||
userColor: 3,
|
||||
};
|
||||
|
||||
export const Moderator = Template.bind({});
|
||||
Moderator.args = {
|
||||
displayName: 'RandomChatter',
|
||||
isAuthorModerator: true,
|
||||
userColor: 2,
|
||||
};
|
||||
42
web/components/chat/ChatPartMessage/ChatPartMessage.tsx
Normal file
42
web/components/chat/ChatPartMessage/ChatPartMessage.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { FC } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { ModerationBadge } from '../ChatUserBadge/ModerationBadge';
|
||||
|
||||
import styles from './ChatPartMessage.module.scss';
|
||||
|
||||
// Lazy loaded components
|
||||
|
||||
const TeamOutlined = dynamic(() => import('@ant-design/icons/TeamOutlined'), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export type ChatPartMessageProps = {
|
||||
isAuthorModerator: boolean;
|
||||
userColor: number;
|
||||
displayName: string;
|
||||
};
|
||||
|
||||
export const ChatPartMessage: FC<ChatPartMessageProps> = ({
|
||||
isAuthorModerator,
|
||||
userColor,
|
||||
displayName,
|
||||
}) => {
|
||||
const color = `var(--theme-color-users-${userColor})`;
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<span style={{ color }}>
|
||||
<span className={styles.icon}>
|
||||
<TeamOutlined />
|
||||
</span>
|
||||
<span className={styles.user}>{displayName}</span>
|
||||
{isAuthorModerator && (
|
||||
<span className={styles.moderatorBadge}>
|
||||
<ModerationBadge userColor={userColor} />
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
left the chat.
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -322,6 +322,9 @@ export const ClientConfigStore: FC = () => {
|
||||
case MessageType.USER_JOINED:
|
||||
setChatMessages(currentState => [...currentState, message as ChatEvent]);
|
||||
break;
|
||||
case MessageType.USER_PARTED:
|
||||
setChatMessages(currentState => [...currentState, message as ChatEvent]);
|
||||
break;
|
||||
case MessageType.SYSTEM:
|
||||
setChatMessages(currentState => [...currentState, message as ChatEvent]);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user