Few changes to chat.
Changed the way the background is set on self sent messages and some styling. Fixed chat container not scrolling. Added 'go to bottom' button.
This commit is contained in:
parent
43aba0a67c
commit
455d8f8169
@ -1,8 +1,16 @@
|
||||
|
||||
.chatHeader {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
padding: 5px 0;
|
||||
color: var(--text-color-secondary);
|
||||
border-bottom: 1px solid var(--color-owncast-gray-700);
|
||||
font-variant: small-caps;
|
||||
}
|
||||
|
||||
.toBottomWrap {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { Spin } from 'antd';
|
||||
import { Button, Spin } from 'antd';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
|
||||
import { useState, useMemo, useRef } from 'react';
|
||||
import { LoadingOutlined, VerticalAlignBottomOutlined } from '@ant-design/icons';
|
||||
import { MessageType, NameChangeEvent } from '../../../interfaces/socket-events';
|
||||
import s from './ChatContainer.module.scss';
|
||||
import { ChatMessage } from '../../../interfaces/chat-message.model';
|
||||
@ -19,6 +18,8 @@ interface Props {
|
||||
export default function ChatContainer(props: Props) {
|
||||
const { messages, loading, usernameToHighlight, chatUserId, isModerator } = props;
|
||||
|
||||
const [atBottom, setAtBottom] = useState(false);
|
||||
// const [showButton, setShowButton] = useState(false);
|
||||
const chatContainerRef = useRef(null);
|
||||
const spinIcon = <LoadingOutlined style={{ fontSize: '32px' }} spin />;
|
||||
|
||||
@ -34,12 +35,12 @@ export default function ChatContainer(props: Props) {
|
||||
);
|
||||
};
|
||||
|
||||
const getViewForMessage = message => {
|
||||
const getViewForMessage = (message: ChatMessage | NameChangeEvent) => {
|
||||
switch (message.type) {
|
||||
case MessageType.CHAT:
|
||||
return (
|
||||
<ChatUserMessage
|
||||
message={message}
|
||||
message={message as ChatMessage}
|
||||
showModeratorMenu={isModerator} // Moderators have access to an additional menu
|
||||
highlightString={usernameToHighlight} // What to highlight in the message
|
||||
renderAsPersonallySent={message.user?.id === chatUserId} // The local user sent this message
|
||||
@ -47,7 +48,7 @@ export default function ChatContainer(props: Props) {
|
||||
/>
|
||||
);
|
||||
case MessageType.NAME_CHANGE:
|
||||
return getNameChangeViewForMessage(message);
|
||||
return getNameChangeViewForMessage(message as NameChangeEvent);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -55,17 +56,36 @@ export default function ChatContainer(props: Props) {
|
||||
|
||||
const MessagesTable = useMemo(
|
||||
() => (
|
||||
<Virtuoso
|
||||
style={{ height: '70vh' }}
|
||||
ref={chatContainerRef}
|
||||
initialTopMostItemIndex={999} // Force alignment to bottom
|
||||
data={messages}
|
||||
itemContent={(index, message) => getViewForMessage(message)}
|
||||
followOutput="auto"
|
||||
alignToBottom
|
||||
/>
|
||||
<>
|
||||
<Virtuoso
|
||||
style={{ height: '75vh' }}
|
||||
ref={chatContainerRef}
|
||||
initialTopMostItemIndex={messages.length - 1} // Force alignment to bottom
|
||||
data={messages}
|
||||
itemContent={(_, message) => getViewForMessage(message)}
|
||||
followOutput="auto"
|
||||
alignToBottom
|
||||
atBottomStateChange={bottom => setAtBottom(bottom)}
|
||||
/>
|
||||
{!atBottom && (
|
||||
<div className={s.toBottomWrap}>
|
||||
<Button
|
||||
ghost
|
||||
icon={<VerticalAlignBottomOutlined />}
|
||||
onClick={() =>
|
||||
chatContainerRef.current.scrollToIndex({
|
||||
index: messages.length - 1,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}
|
||||
>
|
||||
Go to last message
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
[messages, usernameToHighlight, chatUserId, isModerator],
|
||||
[messages, usernameToHighlight, chatUserId, isModerator, atBottom],
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -1,9 +1,20 @@
|
||||
.root {
|
||||
position: relative;
|
||||
font-size: 0.9rem;
|
||||
padding: 5px;
|
||||
padding: 5px 15px 5px 5px;
|
||||
padding-left: 1rem;
|
||||
margin: 8px 5px;
|
||||
// animation: chatFadeIn .1s ease-in;
|
||||
.background {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: currentColor;
|
||||
opacity: 0.07;
|
||||
border-radius: .25rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.user {
|
||||
font: var(--theme-header-font-family);
|
||||
color: var(--color-owncast-grey-100);
|
||||
@ -56,3 +67,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes chatFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@ export default function ChatUserMessage({
|
||||
showModeratorMenu,
|
||||
renderAsPersonallySent, // Move the border to the right and render a background
|
||||
}: Props) {
|
||||
const [bgColor, setBgColor] = useState<string>();
|
||||
const { body, user, timestamp } = message;
|
||||
const { displayName, displayColor } = user;
|
||||
|
||||
@ -28,38 +27,29 @@ export default function ChatUserMessage({
|
||||
const formattedTimestamp = `Sent at ${formatTimestamp(timestamp)}`;
|
||||
const [formattedMessage, setFormattedMessage] = useState<string>(body);
|
||||
|
||||
useEffect(() => {
|
||||
if (renderAsPersonallySent) setBgColor(getBgColor(displayColor));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setFormattedMessage(he.decode(body));
|
||||
}, [message]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(s.root, {
|
||||
[s.ownMessage]: renderAsPersonallySent,
|
||||
})}
|
||||
data-display-color={displayColor}
|
||||
style={{ borderColor: color, backgroundColor: bgColor }}
|
||||
title={formattedTimestamp}
|
||||
>
|
||||
<div className={s.user} style={{ color }}>
|
||||
{displayName}
|
||||
<div style={{ padding: 5 }}>
|
||||
<div
|
||||
className={cn(s.root, {
|
||||
[s.ownMessage]: renderAsPersonallySent,
|
||||
})}
|
||||
style={{ borderColor: color }}
|
||||
title={formattedTimestamp}
|
||||
>
|
||||
<div className={s.user} style={{ color }}>
|
||||
{displayName}
|
||||
</div>
|
||||
<Highlight search={highlightString}>
|
||||
<div className={s.message}>{formattedMessage}</div>
|
||||
</Highlight>
|
||||
{showModeratorMenu && <div>Moderator menu</div>}
|
||||
<div className={s.customBorder} style={{ color }} />
|
||||
<div className={s.background} style={{ color }} />
|
||||
</div>
|
||||
<Highlight search={highlightString}>
|
||||
<div className={s.message}>{formattedMessage}</div>
|
||||
</Highlight>
|
||||
{showModeratorMenu && <div>Moderator menu</div>}
|
||||
<div className={s.customBorder} style={{ color }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getBgColor(displayColor: number, alpha = 13) {
|
||||
const root = document.querySelector(':root');
|
||||
const css = getComputedStyle(root);
|
||||
const hexColor = css.getPropertyValue(`--theme-user-colors-${displayColor}`);
|
||||
return hexColor.concat(alpha.toString());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user