diff options
Diffstat (limited to 'web/react/components/post_body.jsx')
-rw-r--r-- | web/react/components/post_body.jsx | 186 |
1 files changed, 122 insertions, 64 deletions
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 61a0c3e2d..dcbe56399 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -1,26 +1,30 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -const FileAttachmentList = require('./file_attachment_list.jsx'); -const UserStore = require('../stores/user_store.jsx'); -const Utils = require('../utils/utils.jsx'); -const Constants = require('../utils/constants.jsx'); -const TextFormatting = require('../utils/text_formatting.jsx'); -const twemoji = require('twemoji'); -const PostBodyAdditionalContent = require('./post_body_additional_content.jsx'); +import FileAttachmentList from './file_attachment_list.jsx'; +import UserStore from '../stores/user_store.jsx'; +import * as Utils from '../utils/utils.jsx'; +import * as Emoji from '../utils/emoticons.jsx'; +import Constants from '../utils/constants.jsx'; +const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES; +import * as TextFormatting from '../utils/text_formatting.jsx'; +import twemoji from 'twemoji'; +import PostBodyAdditionalContent from './post_body_additional_content.jsx'; + +import providers from './providers.json'; export default class PostBody extends React.Component { constructor(props) { super(props); this.receivedYoutubeData = false; - this.isGifLoading = false; + this.isImgLoading = false; this.handleUserChange = this.handleUserChange.bind(this); this.parseEmojis = this.parseEmojis.bind(this); this.createEmbed = this.createEmbed.bind(this); - this.createGifEmbed = this.createGifEmbed.bind(this); - this.loadGif = this.loadGif.bind(this); + this.createImageEmbed = this.createImageEmbed.bind(this); + this.loadImg = this.loadImg.bind(this); this.createYoutubeEmbed = this.createYoutubeEmbed.bind(this); const linkData = Utils.extractLinks(this.props.post.message); @@ -29,6 +33,7 @@ export default class PostBody extends React.Component { this.state = { links: linkData.links, message: linkData.text, + post: this.props.post, hasUserProfiles: profiles && Object.keys(profiles).length > 1 }; } @@ -49,7 +54,17 @@ export default class PostBody extends React.Component { } parseEmojis() { - twemoji.parse(ReactDOM.findDOMNode(this), {size: Constants.EMOJI_SIZE}); + twemoji.parse(ReactDOM.findDOMNode(this), { + className: 'emoji twemoji', + base: '', + folder: Emoji.getImagePathForEmoticon() + }); + } + + componentWillMount() { + if (this.props.post.filenames.length === 0 && this.state.links && this.state.links.length > 0) { + this.embed = this.createEmbed(this.state.links[0]); + } } componentDidMount() { @@ -76,47 +91,86 @@ export default class PostBody extends React.Component { componentWillReceiveProps(nextProps) { const linkData = Utils.extractLinks(nextProps.post.message); + if (this.props.post.filenames.length === 0 && this.state.links && this.state.links.length > 0) { + this.embed = this.createEmbed(linkData.links[0]); + } this.setState({links: linkData.links, message: linkData.text}); } createEmbed(link) { - let embed = this.createYoutubeEmbed(link); + const post = this.state.post; + + if (!link) { + if (post.type === 'oEmbed') { + post.props.oEmbedLink = ''; + post.type = ''; + } + return null; + } + + const trimmedLink = link.trim(); + + if (Utils.isFeatureEnabled(PreReleaseFeatures.EMBED_PREVIEW)) { + const provider = this.getOembedProvider(trimmedLink); + if (provider != null) { + post.props.oEmbedLink = trimmedLink; + post.type = 'oEmbed'; + this.setState({post, provider}); + return ''; + } + } + + const embed = this.createYoutubeEmbed(link); if (embed != null) { return embed; } - embed = this.createGifEmbed(link); + for (let i = 0; i < Constants.IMAGE_TYPES.length; i++) { + const imageType = Constants.IMAGE_TYPES[i]; + const suffix = link.substring(link.length - (imageType.length + 1)); + if (suffix === '.' + imageType || suffix === '=' + imageType) { + return this.createImageEmbed(link, this.state.imgLoaded); + } + } + + return null; + } - return embed; + getOembedProvider(link) { + for (let i = 0; i < providers.length; i++) { + for (let j = 0; j < providers[i].patterns.length; j++) { + if (link.match(providers[i].patterns[j])) { + return providers[i]; + } + } + } + return null; } - loadGif(src) { - if (this.isGifLoading) { + loadImg(src) { + if (this.isImgLoading) { return; } - this.isGifLoading = true; + this.isImgLoading = true; - const gif = new Image(); - gif.onload = ( + const img = new Image(); + img.onload = ( () => { - this.setState({gifLoaded: true}); + this.embed = this.createImageEmbed(src, true); + this.setState({imgLoaded: true}); } ); - gif.src = src; + img.src = src; } - createGifEmbed(link) { - if (link.substring(link.length - 4) !== '.gif') { - return null; - } - - if (!this.state.gifLoaded) { - this.loadGif(link); + createImageEmbed(link, isLoaded) { + if (!isLoaded) { + this.loadImg(link); return ( <img - className='gif-div placeholder' + className='img-div placeholder' height='500px' /> ); @@ -124,7 +178,7 @@ export default class PostBody extends React.Component { return ( <img - className='gif-div' + className='img-div' src={link} /> ); @@ -133,7 +187,7 @@ export default class PostBody extends React.Component { handleYoutubeTime(link) { const timeRegex = /[\\?&]t=([0-9hms]+)/; - const time = link.trim().match(timeRegex); + const time = link.match(timeRegex); if (!time || !time[1]) { return ''; } @@ -160,14 +214,14 @@ export default class PostBody extends React.Component { } createYoutubeEmbed(link) { - const ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/; + const ytRegex = /(?:http|https):\/\/(?:www\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(\/u\/\w\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^\/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#\&\?]*)/; const match = link.trim().match(ytRegex); - if (!match || match[1].length !== 11) { + if (!match || match[2].length !== 11) { return null; } - const youtubeId = match[1]; + const youtubeId = match[2]; const time = this.handleYoutubeTime(link); function onClick(e) { @@ -212,7 +266,7 @@ export default class PostBody extends React.Component { } return ( - <div className='post-comment'> + <div> <h4> <span className='video-type'>{header}</span> <span className='video-title'><a href={link}>{this.state.youtubeTitle}</a></span> @@ -255,7 +309,15 @@ export default class PostBody extends React.Component { let apostrophe = ''; let name = '...'; if (profile != null) { - if (profile.username.slice(-1) === 's') { + let username = profile.username; + if (parentPost.props && + parentPost.props.from_webhook && + parentPost.props.override_username && + global.window.mm_config.EnablePostUsernameOverride === 'true') { + username = parentPost.props.override_username; + } + + if (username.slice(-1) === 's') { apostrophe = '\''; } else { apostrophe = '\'s'; @@ -263,9 +325,9 @@ export default class PostBody extends React.Component { name = ( <a className='theme' - onClick={Utils.searchForTerm.bind(null, profile.username)} + onClick={Utils.searchForTerm.bind(null, username)} > - {profile.username} + {username} </a> ); } @@ -284,7 +346,7 @@ export default class PostBody extends React.Component { } comment = ( - <p className='post-link'> + <div className='post__link'> <span> {'Commented on '}{name}{apostrophe}{' message: '} <a @@ -294,15 +356,13 @@ export default class PostBody extends React.Component { {message} </a> </span> - </p> + </div> ); - - postClass += ' post-comment'; } let loading; if (post.state === Constants.POST_FAILED) { - postClass += ' post-fail'; + postClass += ' post--fail'; loading = ( <a className='theme post-retry pull-right' @@ -322,11 +382,6 @@ export default class PostBody extends React.Component { ); } - let embed; - if (filenames.length === 0 && this.state.links && this.state.links.length > 0) { - embed = this.createEmbed(this.state.links[0]); - } - let fileAttachmentHolder = ''; if (filenames && filenames.length > 0) { fileAttachmentHolder = ( @@ -339,25 +394,28 @@ export default class PostBody extends React.Component { } return ( - <div className='post-body'> + <div> {comment} - <div - key={`${post.id}_message`} - id={`${post.id}_message`} - className={postClass} - > - {loading} - <span - ref='message_span' - onClick={TextFormatting.handleClick} - dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.message)}} + <div className='post__body'> + <div + key={`${post.id}_message`} + id={`${post.id}_message`} + className={postClass} + > + {loading} + <span + ref='message_span' + onClick={TextFormatting.handleClick} + dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.state.message)}} + /> + </div> + <PostBodyAdditionalContent + post={this.state.post} + provider={this.state.provider} /> + {fileAttachmentHolder} + {this.embed} </div> - <PostBodyAdditionalContent - post={post} - /> - {fileAttachmentHolder} - {embed} </div> ); } |