Compare commits

..

No commits in common. "22ef8bb29c224f9483f87229e842ef5dd6095f7b" and "85870d1f7d43802ff17d471e318aafc5be08a0d6" have entirely different histories.

5 changed files with 26 additions and 82 deletions

View File

@ -2,19 +2,19 @@ import { useEffect, useState, useRef } from 'react';
import ChatLog from '../chat-log'; import ChatLog from '../chat-log';
const maxsize = 25; const maxsize = 100;
function App() { function App() {
const [messages, setMessages] = useState(Array(maxsize).fill(null)); const [messages, setMessages] = useState([]);
const socket = useRef(null); const socket = useRef(null);
useEffect(() => { useEffect(() => {
const onEvent = function (event) { const onEvent = function (event) {
setMessages((state) => { setMessages((state) => {
const newState = [...state]; if (state.length >= maxsize) {
newState.unshift(JSON.parse(event.data)); state.pop();
newState.pop(); }
return newState; return [JSON.parse(event.data), ...state];
}); });
}; };

View File

@ -1,36 +1,26 @@
import { useRef, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Message from './message'; import Message from './message';
import { List, Spacer } from './chat-log.style'; import { List } from './chat-log.style';
const elementMap = { const elementMap = {
Message, Message,
null: Spacer,
}; };
function ChatLog({ function ChatLog({
log, log,
}) { }) {
const listRef = useRef(); // TODO: Should look at full MRO
const renderableEvents = log.map((event) => { const renderableEvents = log.filter((event) => Object.keys(elementMap).includes(event.type[0]))
if (!event?.type) {
event = { type: ['null'], data: { }};
}
const bestRenderableInterface = event.type.find((interfaceName) => Object.keys(elementMap).includes(interfaceName));
const renderer = elementMap[bestRenderableInterface];
return [event, renderer];
}).filter(([event, renderer]) => !!renderer);
useEffect(() => {
listRef.current.scrollTop = -1;
}, [renderableEvents]);
return ( return (
<List ref={listRef}> <List>
{renderableEvents.map(([event, Element]) => ( {renderableEvents.map((event) => {
<Element key={event.data.id} {...event.data} /> const Element = elementMap[event.type[0]];
))} return (
<Element key={event.data.id} {...event.data} />
);
})}
</List> </List>
); );
} }

View File

@ -2,7 +2,6 @@ import styled from 'styled-components';
export const List = styled.div` export const List = styled.div`
height: 100%; height: 100%;
position: relative;
display: flex; display: flex;
flex-direction: column-reverse; flex-direction: column-reverse;
align-items: start; align-items: start;
@ -10,17 +9,4 @@ export const List = styled.div`
gap: 16px; gap: 16px;
padding: 12px 8px; padding: 12px 8px;
overflow: scroll;
scroll-behavior: smooth;
scrollbar-width: none;
&::-webkit-scrollbar {
width: 0px;
background: transparent;
}
`;
export const Spacer = styled.div`
min-height: 2em;
`; `;

View File

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import { XTERM_COLORS } from '../../../index.style'; import { XTERM_COLORS } from '../../../index.style';
import { Wrapper, Author, Body, Emote } from './message.style'; import { Wrapper, Author } from './message.style';
const stringHashcode = (str) => { const stringHashcode = (str) => {
var hash = 0, i, chr; var hash = 0, i, chr;
@ -15,29 +15,17 @@ const stringHashcode = (str) => {
return hash; return hash;
} }
const emoteRegex = /:([^:\s]+):/g;
function Message({ function Message({
user_id, user_name, user_type, user_color, text, emotes, user_id, user_name, user_type, user_color, text,
deemphasize, deemphasize,
}) { }) {
const fallbackColor = XTERM_COLORS[(stringHashcode(user_id.toString()) % 144) + 88]; const fallbackColor = XTERM_COLORS[(stringHashcode(user_id.toString()) % 144) + 88];
return ( return (
<Wrapper type={user_type} color={user_color ?? fallbackColor} deemphasize={deemphasize}> <Wrapper type={user_type} color={user_color ?? fallbackColor} deemphasize={deemphasize}>
<Author> <Author>
[ {user_name} ] [ {user_name} ]
</Author> </Author>
<Body> {text}
{text.split(emoteRegex).map((textOrEmote, index) => {
if (textOrEmote === "") return null;
if (index % 2 === 0) {
return <span>{textOrEmote}</span>;
} else {
return <Emote src={emotes[textOrEmote]} />;
}
}).filter((item) => item !== null)}
</Body>
</Wrapper> </Wrapper>
); );
} }
@ -47,16 +35,12 @@ Message.propTypes = {
user_name: PropTypes.node.isRequired, user_name: PropTypes.node.isRequired,
user_type: PropTypes.string.isRequired, user_type: PropTypes.string.isRequired,
user_color: PropTypes.string, user_color: PropTypes.string,
text: PropTypes.node.isRequired,
emotes: PropTypes.shape({
[PropTypes.string]: PropTypes.string,
}),
deemphasize: PropTypes.bool, deemphasize: PropTypes.bool,
text: PropTypes.node.isRequired,
}; };
Message.defaultProps = { Message.defaultProps = {
user_color: undefined, user_color: undefined,
emotes: {},
deemphasize: false, deemphasize: false,
}; };

View File

@ -7,16 +7,6 @@ export const Author = styled.div`
margin-bottom: 4px; margin-bottom: 4px;
`; `;
export const Body = styled.span`
`;
export const Emote = styled.img`
width: 2em;
height: 2em;
vertical-align: middle;
`;
export const normalizeColor = (color) => { export const normalizeColor = (color) => {
if (color === null) { if (color === null) {
return 'inherit'; return 'inherit';
@ -73,24 +63,18 @@ export const Wrapper = styled.div`
width: 100%; width: 100%;
overflow-wrap: break-word; overflow-wrap: break-word;
@keyframes fade { @keyframes dissapear {
0% { 0% {
opacity: 1; opacity: 1;
} }
15% {
opacity: 1;
}
100% { 100% {
opacity: 0; opacity: 0;
} }
} }
@keyframes slide { animation: 160s dissapear linear;
from {
transform: translateX(-50px);
}
to {
transform: translateX(0);
}
}
animation: 160s fade cubic-bezier(.6,.04,.98,.34), 0.3s slide cubic-bezier(.08,.82,.17,1);
animation-fill-mode: forwards; animation-fill-mode: forwards;
`; `;