feat: initial messages

This commit is contained in:
cha0s 2020-07-13 21:44:11 -05:00
parent b9b439290e
commit 7982eb054a
6 changed files with 275 additions and 2 deletions

View File

@ -2,9 +2,13 @@ import './chat--center.scss';
import React from 'react'; import React from 'react';
import ChatMessages from './chat--messages';
export default function ChatCenter() { export default function ChatCenter() {
return ( return (
<div className="chat__center flexed" /> <div className="center flexed">
<ChatMessages />
</div>
); );
} }

View File

@ -1,3 +1,3 @@
.chat__center { .center {
flex-grow: 1; flex-grow: 1;
} }

View File

@ -0,0 +1,56 @@
import './chat--message.scss';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import ReactMarkdown from 'react-markdown';
export default function PlayersChatMessageSpace(props) {
const {message: {owner, text, timestamp}, isShort} = props;
const ownerMap = {
1: 'cha0s',
2: 'BabeHasHiccups',
};
const dtf = new Intl.DateTimeFormat(undefined, {
hour: '2-digit',
minute: '2-digit',
});
const date = new Date(timestamp);
const $messageTime = (
<div
className="chat--messageTime"
title={date.toISOString()}
>
{dtf.format(date)}
</div>
);
return (
<div
className={classnames('chat--message', {short: isShort})}
>
{
!isShort && (
<header>
<div className="chat--messageOwner">{ownerMap[owner]}</div>
{$messageTime}
</header>
)
}
<div className="chat--messageText">
<div className="chat--messageMarkdown">
<ReactMarkdown source={text} />
</div>
{isShort && $messageTime}
</div>
</div>
);
}
PlayersChatMessageSpace.propTypes = {
isShort: PropTypes.bool.isRequired,
message: PropTypes.shape({
text: PropTypes.string,
timestamp: PropTypes.number,
owner: PropTypes.string,
}).isRequired,
};

View File

@ -0,0 +1,82 @@
.chat--message {
margin-top: 0.75rem;
padding: 0.25rem 1rem;
}
.chat--message, .chat--message * {
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
}
.chat--message:first-child {
margin-top: 0;
}
.chat--message.short {
margin-top: 0;
margin-bottom: 0;
}
.chat--message.short .chat--messageTime {
display: inline;
visibility: hidden;
}
.chat--message.short:hover .chat--messageTime {
visibility: visible;
}
.chat--message.short .chat--messageOwner {
display: none;
}
.chat--message:hover {
background-color: #1c1c1c;
}
header {
display: flex;
width: 100%;
}
.chat--messageOwner {
color: #d68030aa;
font-family: Caladea, 'Times New Roman', Times, serif;
font-weight: bold;
margin-bottom: 0.25rem;
}
.chat--messageTime {
color: #666;
font-size: 0.7rem;
margin-left: 0.5rem;
}
.chat--message:not(.short) .chat--messageTime {
line-height: 1.25rem;
}
.chat--messageText {
font-size: 0.9rem;
}
.chat--messageMarkdown * {
margin: 0;
}
.chat--messageMarkdown {
h1, h2, h3, h4, h5, h6 {
line-height: 1rem;
}
}
.chat--messageMarkdown {
display: inline;
}
.chat--messageMarkdown :first-child {
margin-top: 0;
}
.chat--messageMarkdown :last-child {
display: inline;
}

View File

@ -0,0 +1,103 @@
import './chat--messages.scss';
import React, {useLayoutEffect, useRef, useState} from 'react';
import ChatMessage from './chat--message';
export default function PlayersChatSpace() {
const [$form, $messages] = [useRef(null), useRef(null)];
const [text, setText] = useState('');
const messages = [
{
key: 1,
owner: 1,
text: 'Hi!',
timestamp: Date.now(),
},
{
key: 2,
owner: 2,
text: 'Yo.',
timestamp: Date.now(),
},
{
key: 3,
owner: 2,
text: 'How have you been?',
timestamp: Date.now(),
},
{
key: 4,
owner: 1,
text: 'Not too bad.',
timestamp: Date.now(),
},
];
const {current} = $messages;
const isAtTheBottom = !current
? true
: 0 === current.scrollTop || (
current.scrollHeight
=== current.scrollTop
+ ['margin-top', 'margin-bottom'].reduce((r, property) => (
r + parseInt(window.getComputedStyle(current).getPropertyValue(property), 10)
), current.offsetHeight)
);
const heightWatch = current && current.scrollHeight;
const messageCount = messages && messages.length;
useLayoutEffect(() => {
if (isAtTheBottom) {
current?.scrollTo(0, !current ? 0 : current.scrollHeight);
}
}, [current, heightWatch, messageCount, isAtTheBottom]);
let messageOwner = false;
return (
<div className="chat--messages">
<div className="chat--messagesSmoosh" />
<div
className="chat--messagesList"
ref={$messages}
>
{messages && messages.map((message) => {
const $message = (
<ChatMessage
key={message.key}
isShort={messageOwner === message.owner}
message={message}
/>
);
messageOwner = message.owner;
return $message;
})}
</div>
<form
onSubmit={(event) => {
event.preventDefault();
const trimmed = text.slice(0, 1000).trim();
if (trimmed) {
// socket.send(new ActionPacket(chat({owner: playerId, text: trimmed}, {game: id})));
}
setText('');
}}
ref={$form}
>
<textarea
className="chat--messagesTextarea"
name="message"
type="textarea"
maxLength="1000"
onChange={(event) => {
setText(event.target.value);
}}
onKeyDown={(event) => {
if ('Enter' === event.key && !event.shiftKey) {
$form.current?.dispatchEvent(new Event('submit', {cancelable: true}));
event.preventDefault();
}
}}
value={text}
/>
</form>
</div>
);
}

View File

@ -0,0 +1,28 @@
.chat--messages {
display: flex;
flex-direction: column;
overflow: hidden;
height: 100%;
}
.chat--messagesSmoosh {
flex-grow: 1;
}
.chat--messagesList {
overflow-y: auto;
}
.chat--messagesTextarea {
background-color: #333;
border: none;
border-radius: 10px;
color: #aaa;
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
font-size: 0.9rem;
height: 4rem;
margin: 0.5rem 1rem 1rem;
padding: 0.5rem;
resize: none;
width: calc(100% - 2rem);
}