add mentions
This commit is contained in:
parent
a7b43c2854
commit
d278edb9b1
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user