mirror of https://github.com/status-im/chat.git
refactor ChatBox component into a Message components and other sub components
This commit is contained in:
parent
d50cb5e7c2
commit
cea7f32b0a
|
@ -21,3 +21,5 @@
|
|||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
TODO
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
// @flow
|
||||
import React, { Fragment, PureComponent } from 'react';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import YouTube from 'react-youtube';
|
||||
import Linkify from 'react-linkify';
|
||||
import SpotifyPlayer from 'react-spotify-player';
|
||||
import { Emoji } from 'emoji-mart';
|
||||
import Jazzicon, { jsNumberForAddress } from 'react-jazzicon'
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
import { Matcher } from '@areknawo/rex'
|
||||
import SyntaxLookup from '../utils/syntaxLookup';
|
||||
import { getFile } from '../utils/ipfs';
|
||||
|
||||
const ipfsMatcher = new Matcher().begin().find('/ipfs/');
|
||||
|
||||
// TODO: not exactly bulletproof right now, needs proper regex
|
||||
function hasYoutubeLink(text) {
|
||||
return text.indexOf('http://www.youtube.com') >= 0 || text.indexOf('https://www.youtube.com') >= 0;
|
||||
}
|
||||
|
||||
// TODO: not exactly bulletproof right now, needs proper regex
|
||||
function isSpotifyLink(text) {
|
||||
return text.indexOf('spotify:') >= 0 ;
|
||||
}
|
||||
|
||||
// https://gist.github.com/takien/4077195#
|
||||
function getYoutubeId(url) {
|
||||
let ID = '';
|
||||
url = url.replace(/(>|<)/gi,'').split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/);
|
||||
if (url[2] !== undefined) {
|
||||
ID = url[2].split(/[^0-9a-z_\-]/i);
|
||||
ID = ID[0];
|
||||
}
|
||||
else {
|
||||
ID = url;
|
||||
}
|
||||
return ID;
|
||||
}
|
||||
|
||||
function isImage(text) {
|
||||
return text.indexOf("http") >= 0 && (text.indexOf('.jpg') || text.indexOf('.gif'));
|
||||
}
|
||||
|
||||
// TODO: this needs to be reviewed. best to return as a css background-image instead
|
||||
function displayImage(text) {
|
||||
|
||||
let reg = new RegExp(/\b(https?:\/\/\S+(?:png|jpe?g|gif)\S*)\b/);
|
||||
let imageUrl = reg.exec(text);
|
||||
if (!imageUrl) return (<span></span>);
|
||||
return (<img src={imageUrl[0]} alt="" style={{maxWidth: '90%'}} />)
|
||||
}
|
||||
|
||||
// TODO use regex for code parsing / detection. Add new line detection for shift+enter
|
||||
const MessageRender = ({ message }) => {
|
||||
const emojis = [];
|
||||
let match;
|
||||
const regex1 = RegExp(/:[\-a-zA-Z_+0-9]+:/g);
|
||||
while ((match = regex1.exec(message)) !== null) {
|
||||
emojis.push(<Emoji emoji={match[0]} size={16} />);
|
||||
}
|
||||
|
||||
const parts = message.split(regex1);
|
||||
parts.forEach((part, i) => {
|
||||
parts[i] = <span className="match" key={i}>{part}{emojis[i]}</span>;
|
||||
});
|
||||
|
||||
return (message[2] === "`" && SyntaxLookup[message.slice(0,2)]
|
||||
? <SyntaxHighlighter language={SyntaxLookup[message.slice(0,2)]} style={atomDark}>{message.slice(3)}</SyntaxHighlighter>
|
||||
: <Linkify><span style={{ wordWrap: 'break-word', whiteSpace: 'pre-line' }}>{parts}</span></Linkify>)
|
||||
};
|
||||
|
||||
class ChatBox extends PureComponent {
|
||||
|
||||
state = {
|
||||
imgUrl: null
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { message } = this.props;
|
||||
if (ipfsMatcher.test(message)) this.getImageFromIpfs();
|
||||
}
|
||||
|
||||
getImageFromIpfs = async () => {
|
||||
const { ipfs, message } = this.props;
|
||||
const files = await getFile(ipfs, message);
|
||||
const { content } = files[0];
|
||||
const arrayBufferView = new Uint8Array(content);
|
||||
const blob = new Blob([ arrayBufferView ], { type: "image/jpeg" });
|
||||
const imgUrl = URL.createObjectURL(blob);
|
||||
this.setState({ imgUrl });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { username, message, pubkey } = this.props;
|
||||
const { imgUrl } = this.state;
|
||||
return (
|
||||
<Fragment>
|
||||
<ListItem>
|
||||
<Avatar>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
{pubkey && <Jazzicon diameter={40} seed={jsNumberForAddress(pubkey)}/>}
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
</Avatar>
|
||||
<ListItemText primary={`${username}`} secondary={<MessageRender message={message}/>}/>
|
||||
</ListItem>
|
||||
{hasYoutubeLink(message) &&
|
||||
<ListItem>
|
||||
<YouTube
|
||||
videoId={getYoutubeId(message)}
|
||||
opts={{ height: '390', width: '640', playerVars: { autoplay: 0 } }}
|
||||
/>
|
||||
</ListItem>
|
||||
}
|
||||
{isSpotifyLink(message) &&
|
||||
<ListItem>
|
||||
<SpotifyPlayer
|
||||
uri={message}
|
||||
size={{ 'width': 300, 'height': 300 }}
|
||||
view='list'
|
||||
theme='black'
|
||||
/>
|
||||
</ListItem>
|
||||
}
|
||||
{!!imgUrl && <img src={imgUrl} alt='ipfs' style={{maxWidth: '90%'}} />}
|
||||
{isImage(message) && displayImage(message)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default ChatBox;
|
|
@ -13,7 +13,7 @@ import AddCircle from '@material-ui/icons/AddCircle';
|
|||
|
||||
import 'emoji-mart/css/emoji-mart.css';
|
||||
|
||||
import ChatBox from './ChatBox';
|
||||
import Message from './Message';
|
||||
import ChatHeader from './ChatHeader';
|
||||
import Userlist from './Userlist';
|
||||
import { uploadFileAndSend } from '../utils/ipfs';
|
||||
|
@ -144,7 +144,7 @@ class ChatRoom extends Component {
|
|||
<AutoScrollList style={{ height: messagesHeight, overflow: 'scroll' }}>
|
||||
{messages[currentChannel] && messages[currentChannel].map((message) => (
|
||||
<Fragment key={message.data.payload}>
|
||||
<ChatBox {...message} ipfs={ipfs}/>
|
||||
<Message {...message} ipfs={ipfs}/>
|
||||
<li>
|
||||
<Divider/>
|
||||
</li>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
|
||||
import UserAvatar from './UserAvatar';
|
||||
import MessageRenderer from './messages/MessageRenderer';
|
||||
import YoutubeMessage from './messages/youtube';
|
||||
import SpotifyMessage from './messages/spotify';
|
||||
import ImageMessage from './messages/image';
|
||||
import IpfsImageMessage from './messages/ipfsImage';
|
||||
|
||||
const Message = ({message, pubkey, username, ipfs}) => (
|
||||
<Fragment>
|
||||
<ListItem>
|
||||
<UserAvatar pubkey={pubkey} />
|
||||
<ListItemText primary={`${username}`} secondary={<MessageRenderer message={message}/>}/>
|
||||
</ListItem>
|
||||
<YoutubeMessage message={message} />
|
||||
<SpotifyMessage message={message} />
|
||||
<ImageMessage message={message} />
|
||||
<IpfsImageMessage message={message} ipfs={ipfs} />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
export default Message;
|
|
@ -0,0 +1,14 @@
|
|||
import React from 'react';
|
||||
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Jazzicon, { jsNumberForAddress } from 'react-jazzicon'
|
||||
|
||||
const UserAvatar = ({pubkey}) => (
|
||||
<Avatar>
|
||||
<ListItemAvatar>
|
||||
{pubkey && <Jazzicon diameter={40} seed={jsNumberForAddress(pubkey)}/>}
|
||||
</ListItemAvatar>
|
||||
</Avatar>
|
||||
);
|
||||
|
||||
export default UserAvatar;
|
|
@ -0,0 +1,34 @@
|
|||
import React from 'react';
|
||||
import { Emoji } from 'emoji-mart';
|
||||
import SyntaxLookup from '../../utils/syntaxLookup';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
import Linkify from 'react-linkify';
|
||||
|
||||
const isCode = (message) => {
|
||||
return message[2] === "`" && SyntaxLookup[message.slice(0,2)]
|
||||
}
|
||||
|
||||
// TODO use regex for code parsing / detection. Add new line detection for shift+enter
|
||||
const MessageRenderer = ({ message }) => {
|
||||
if (isCode(message)) {
|
||||
return (<SyntaxHighlighter language={SyntaxLookup[message.slice(0,2)]} style={atomDark}>{message.slice(3)}</SyntaxHighlighter>)
|
||||
}
|
||||
|
||||
const emojis = [];
|
||||
let match;
|
||||
const regex1 = RegExp(/:[\-a-zA-Z_+0-9]+:/g);
|
||||
while ((match = regex1.exec(message)) !== null) {
|
||||
emojis.push(<Emoji emoji={match[0]} size={16} />);
|
||||
}
|
||||
|
||||
const parts = message.split(regex1);
|
||||
parts.forEach((part, i) => {
|
||||
parts[i] = <span className="match" key={i}>{part}{emojis[i]}</span>;
|
||||
});
|
||||
|
||||
return (<Linkify><span style={{ wordWrap: 'break-word', whiteSpace: 'pre-line' }}>{parts}</span></Linkify>)
|
||||
};
|
||||
|
||||
|
||||
export default MessageRenderer;
|
|
@ -0,0 +1,22 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
|
||||
function isImage(text) {
|
||||
return text.indexOf("http") >= 0 && (text.indexOf('.jpg') || text.indexOf('.gif'));
|
||||
}
|
||||
|
||||
// TODO: this needs to be reviewed. best to return as a css background-image instead
|
||||
function displayImage(text) {
|
||||
let reg = new RegExp(/\b(https?:\/\/\S+(?:png|jpe?g|gif)\S*)\b/);
|
||||
let imageUrl = reg.exec(text);
|
||||
if (!imageUrl) return (<span></span>);
|
||||
return (<img src={imageUrl[0]} alt="" style={{maxWidth: '90%'}} />)
|
||||
}
|
||||
|
||||
const ImageMessage = ({message}) => (
|
||||
<Fragment>
|
||||
{isImage(message) && displayImage(message)}
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
export default ImageMessage;
|
|
@ -0,0 +1,39 @@
|
|||
import React, { Fragment, PureComponent } from 'react';
|
||||
import { Matcher } from '@areknawo/rex'
|
||||
import { getFile } from '../../utils/ipfs';
|
||||
|
||||
const ipfsMatcher = new Matcher().begin().find('/ipfs/');
|
||||
|
||||
class IpfsImageMessage extends PureComponent {
|
||||
|
||||
state = {
|
||||
imgUrl: null
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { message } = this.props;
|
||||
if (ipfsMatcher.test(message)) this.getImageFromIpfs();
|
||||
}
|
||||
|
||||
getImageFromIpfs = async () => {
|
||||
const { ipfs, message } = this.props;
|
||||
const files = await getFile(ipfs, message);
|
||||
const { content } = files[0];
|
||||
const arrayBufferView = new Uint8Array(content);
|
||||
const blob = new Blob([ arrayBufferView ], { type: "image/jpeg" });
|
||||
const imgUrl = URL.createObjectURL(blob);
|
||||
this.setState({ imgUrl });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { imgUrl } = this.state;
|
||||
return (
|
||||
<Fragment>
|
||||
{!!imgUrl && <img src={imgUrl} alt='ipfs' style={{maxWidth: '90%'}} />}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export default IpfsImageMessage;
|
|
@ -0,0 +1,25 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import SpotifyPlayer from 'react-spotify-player';
|
||||
|
||||
// TODO: not exactly bulletproof right now, needs proper regex
|
||||
function isSpotifyLink(text) {
|
||||
return text.indexOf('spotify:') >= 0 ;
|
||||
}
|
||||
|
||||
const SpotifyMessage = ({message}) => (
|
||||
<Fragment>
|
||||
{isSpotifyLink(message) &&
|
||||
<ListItem>
|
||||
<SpotifyPlayer
|
||||
uri={message}
|
||||
size={{ 'width': 300, 'height': 300 }}
|
||||
view='list'
|
||||
theme='black'
|
||||
/>
|
||||
</ListItem>
|
||||
}
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
export default SpotifyMessage;
|
|
@ -0,0 +1,37 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import YouTube from 'react-youtube';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
|
||||
// TODO: not exactly bulletproof right now, needs proper regex
|
||||
function hasYoutubeLink(text) {
|
||||
return text.indexOf('http://www.youtube.com') >= 0 || text.indexOf('https://www.youtube.com') >= 0;
|
||||
}
|
||||
|
||||
// https://gist.github.com/takien/4077195#
|
||||
function getYoutubeId(url) {
|
||||
let ID = '';
|
||||
url = url.replace(/(>|<)/gi,'').split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/);
|
||||
if (url[2] !== undefined) {
|
||||
ID = url[2].split(/[^0-9a-z_\-]/i);
|
||||
ID = ID[0];
|
||||
}
|
||||
else {
|
||||
ID = url;
|
||||
}
|
||||
return ID;
|
||||
}
|
||||
|
||||
const YoutubeMessage = ({message}) => (
|
||||
<Fragment>
|
||||
{hasYoutubeLink(message) &&
|
||||
<ListItem>
|
||||
<YouTube
|
||||
videoId={getYoutubeId(message)}
|
||||
opts={{ height: '390', width: '640', playerVars: { autoplay: 0 } }}
|
||||
/>
|
||||
</ListItem>
|
||||
}
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
export default YoutubeMessage;
|
Loading…
Reference in New Issue