add mentions

This commit is contained in:
HF 2020-05-12 14:00:56 +02:00
parent a7b43c2854
commit d278edb9b1
3 changed files with 93 additions and 22 deletions

View File

@ -13,23 +13,21 @@ import { MAX_CHAT_MESSAGES } from '../core/constants';
import type { State } from '../reducers'; import type { State } from '../reducers';
import ChatInput from './ChatInput'; import ChatInput from './ChatInput';
import { saveSelection, restoreSelection } from '../utils/storeSelection'; import { saveSelection, restoreSelection } from '../utils/storeSelection';
import { colorFromText, splitCoordsInString } from '../core/utils'; import { colorFromText, splitChatMessage } from '../core/utils';
function ChatMessage({ name, text, country }) { function ChatMessage({ name, msgArray, country }) {
if (!name || !text) { if (!name || !msgArray) {
return null; return null;
} }
const msgText = text.trim();
const isInfo = (name === 'info'); const isInfo = (name === 'info');
let className = 'msg'; let className = 'msg';
if (isInfo) { if (isInfo) {
className += ' info'; className += ' info';
} else if (text.charAt(0) === '>') { } else if (msgArray[0][1].charAt(0) === '>') {
className += ' greentext'; className += ' greentext';
} }
const splitMsg = splitCoordsInString(msgText);
return ( return (
<p className="chatmsg"> <p className="chatmsg">
@ -60,26 +58,35 @@ function ChatMessage({ name, text, country }) {
) )
} }
{ {
splitMsg.map((txt, i) => { msgArray.map((msgPart) => {
if (i % 2 === 0) { const [type, txt] = msgPart;
if (type === 't') {
return (<span className={className}>{txt}</span>);
} if (type === 'c') {
return (<a href={`./${txt}`}>{txt}</a>);
} if (type === 'p') {
return (<span className="ping">{txt}</span>);
} if (type === 'm') {
return ( return (
<span <span
className={className} className="mention"
> style={{
{txt} color: colorFromText(txt),
</span> }}
>{txt}</span>
); );
} }
return (<a href={`./${txt}`}>{txt}</a>); return null;
}) })
} }
</p> </p>
); );
} }
const Chat = ({ chatMessages, chatChannel }) => { const Chat = ({ chatMessages, chatChannel, ownName }) => {
const listRef = useRef(); const listRef = useRef();
const [selection, setSelection] = useState(null); const [selection, setSelection] = useState(null);
const [nameRegExp, setNameRegExp] = useState(null);
const { stayScrolled } = useStayScrolled(listRef, { const { stayScrolled } = useStayScrolled(listRef, {
initialScroll: Infinity, initialScroll: Infinity,
}); });
@ -94,7 +101,14 @@ const Chat = ({ chatMessages, chatChannel }) => {
if (channelMessages.length === MAX_CHAT_MESSAGES) { if (channelMessages.length === MAX_CHAT_MESSAGES) {
restoreSelection(selection); restoreSelection(selection);
} }
}); }, [channelMessages]);
useEffect(() => {
const regExp = (ownName)
? new RegExp(`(^|\\s+)(@${ownName})(\\s+|$)`, 'g')
: null;
setNameRegExp(regExp);
}, [ownName]);
return ( return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}> <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
@ -109,7 +123,7 @@ const Chat = ({ chatMessages, chatChannel }) => {
channelMessages.map((message) => ( channelMessages.map((message) => (
<ChatMessage <ChatMessage
name={message[0]} name={message[0]}
text={message[1]} msgArray={splitChatMessage(message[1], nameRegExp)}
country={message[2]} country={message[2]}
/> />
)) ))
@ -121,9 +135,9 @@ const Chat = ({ chatMessages, chatChannel }) => {
}; };
function mapStateToProps(state: State) { function mapStateToProps(state: State) {
const { chatMessages } = state.user; const { chatMessages, name } = state.user;
const { chatChannel } = state.gui; const { chatChannel } = state.gui;
return { chatMessages, chatChannel }; return { chatMessages, chatChannel, ownName: name };
} }
export default connect(mapStateToProps)(Chat); export default connect(mapStateToProps)(Chat);

View File

@ -227,10 +227,55 @@ export function colorFromText(str: string) {
return `#${'00000'.substring(0, 6 - c.length)}${c}`; return `#${'00000'.substring(0, 6 - c.length)}${c}`;
} }
/*
* splits chat message into array of what it represents
* [[type, text],[type, text], ...]
* type:
* 't': text
* 'p': ping
* 'c': coordinates
* 'm': mention of somebody else
* nameRegExp has to be in the form of:
new RegExp(`(^|\\s+)(@${ownName})(\\s+|$)`, 'g');
*/
const linkRegExp = /(#[a-z]*,-?[0-9]*,-?[0-9]*(,-?[0-9]+)?)/gi; const linkRegExp = /(#[a-z]*,-?[0-9]*,-?[0-9]*(,-?[0-9]+)?)/gi;
export function splitCoordsInString(text) { const linkRegExpFilter = (val, ind) => ((ind % 3) !== 2);
const arr = text const mentionRegExp = /(^|\s+)(@\S+)(\s+|$)/g;
.split(linkRegExp) const spaceFilter = (val) => (val !== ' ');
.filter((val, ind) => ((ind % 3) !== 2));
function splitChatMessageRegexp(
msgArray,
regExp,
ident,
filter = () => true,
) {
return msgArray.map((msgPart) => {
const [type, part] = msgPart;
if (type !== 't') {
return [msgPart];
}
return part
.split(regExp)
.filter(filter)
.map((stri, i) => {
if (i % 2 === 0) {
return ['t', stri];
}
return [ident, stri];
})
.filter((el) => !!el)
}).flat(1);
}
export function splitChatMessage(message, nameRegExp = null) {
if (!message) {
return null;
}
let arr = [['t', message.trim()]];
arr = splitChatMessageRegexp(arr, linkRegExp, 'c', linkRegExpFilter);
if (nameRegExp) {
arr = splitChatMessageRegexp(arr, nameRegExp, 'p', spaceFilter);
}
arr = splitChatMessageRegexp(arr, mentionRegExp, 'm', spaceFilter);
return arr; return arr;
} }

View File

@ -403,6 +403,18 @@ tr:nth-child(even) {
.msg.greentext{ .msg.greentext{
color: green; color: green;
} }
.ping {
background-color: #ffff87;
border-style: solid;
border-color: black;
border-width: 1px;
color: #1d1c1c;
border-radius: 6px;
}
.mention, .ping {
margin-left: 5px;
margin-right: 5px;
}
#chatlink { #chatlink {
position: absolute; position: absolute;
font-weight: bold; font-weight: bold;