diff options
Diffstat (limited to 'web/react')
-rw-r--r-- | web/react/components/post_body.jsx | 137 | ||||
-rw-r--r-- | web/react/components/post_body_additional_content.jsx | 103 | ||||
-rw-r--r-- | web/react/components/post_image.jsx | 81 | ||||
-rw-r--r-- | web/react/components/posts_view.jsx | 2 | ||||
-rw-r--r-- | web/react/components/rhs_thread.jsx | 5 | ||||
-rw-r--r-- | web/react/components/search_results.jsx | 10 | ||||
-rw-r--r-- | web/react/components/search_results_item.jsx | 8 |
7 files changed, 173 insertions, 173 deletions
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 506b38ce6..70cf86748 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -6,13 +6,9 @@ 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 YoutubeVideo from './youtube_video.jsx'; - -import providers from './providers.json'; import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl'; @@ -31,19 +27,7 @@ class PostBody extends React.Component { constructor(props) { super(props); - this.isImgLoading = false; - this.parseEmojis = this.parseEmojis.bind(this); - this.createEmbed = this.createEmbed.bind(this); - this.createImageEmbed = this.createImageEmbed.bind(this); - this.loadImg = this.loadImg.bind(this); - - const linkData = Utils.extractLinks(this.props.post.message); - - this.state = { - links: linkData, - post: this.props.post - }; } getAllChildNodes(nodeIn) { @@ -69,120 +53,10 @@ class PostBody extends React.Component { }); } - 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() { this.parseEmojis(); } - componentDidUpdate() { - this.parseEmojis(); - } - - 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[0]); - } - this.setState({ - links: linkData - }); - } - - createEmbed(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 ''; - } - } - - if (YoutubeVideo.isYoutubeLink(link)) { - return ( - <YoutubeVideo - channelId={post.channel_id} - link={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; - } - - 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; - } - - loadImg(src) { - if (this.isImgLoading) { - return; - } - - this.isImgLoading = true; - - const img = new Image(); - img.onload = ( - () => { - this.embed = this.createImageEmbed(src, true); - this.setState({imgLoaded: true}); - } - ); - img.src = src; - } - - createImageEmbed(link, isLoaded) { - if (!isLoaded) { - this.loadImg(link); - return ( - <img - className='img-div placeholder' - height='500px' - /> - ); - } - - return ( - <img - className='img-div' - src={link} - /> - ); - } - render() { const {formatMessage} = this.props.intl; const post = this.props.post; @@ -295,6 +169,7 @@ class PostBody extends React.Component { } let message; + let additionalContent = null; if (this.props.post.state === Constants.POST_DELETED) { message = ( <FormattedMessage @@ -309,6 +184,10 @@ class PostBody extends React.Component { dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message)}} /> ); + + additionalContent = ( + <PostBodyAdditionalContent post={this.props.post}/> + ); } return ( @@ -323,12 +202,8 @@ class PostBody extends React.Component { {loading} {message} </div> - <PostBodyAdditionalContent - post={this.state.post} - provider={this.state.provider} - /> {fileAttachmentHolder} - {this.embed} + {additionalContent} </div> </div> ); diff --git a/web/react/components/post_body_additional_content.jsx b/web/react/components/post_body_additional_content.jsx index 4871eea4f..a76c59fb3 100644 --- a/web/react/components/post_body_additional_content.jsx +++ b/web/react/components/post_body_additional_content.jsx @@ -3,72 +3,103 @@ import PostAttachmentList from './post_attachment_list.jsx'; import PostAttachmentOEmbed from './post_attachment_oembed.jsx'; +import PostImage from './post_image.jsx'; +import YoutubeVideo from './youtube_video.jsx'; + +import Constants from '../utils/constants.jsx'; +import OEmbedProviders from './providers.json'; +import * as Utils from '../utils/utils.jsx'; export default class PostBodyAdditionalContent extends React.Component { constructor(props) { super(props); this.getSlackAttachment = this.getSlackAttachment.bind(this); - this.getOembedAttachment = this.getOembedAttachment.bind(this); - this.getComponent = this.getComponent.bind(this); + this.getOEmbedProvider = this.getOEmbedProvider.bind(this); } - componentWillMount() { - this.setState({type: this.props.post.type, shouldRender: Boolean(this.props.post.type)}); + shouldComponentUpdate(nextProps) { + if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) { + return true; + } + + return false; } getSlackAttachment() { - const attachments = this.props.post.props && this.props.post.props.attachments || []; + let attachments = []; + if (this.props.post.props && this.props.post.props.attachments) { + attachments = this.props.post.props.attachments; + } + return ( <PostAttachmentList - key={'post_body_additional_content' + this.props.post.id} attachments={attachments} /> ); } - getOembedAttachment() { - const link = this.props.post.props && this.props.post.props.oEmbedLink || ''; - return ( - <PostAttachmentOEmbed - key={'post_body_additional_content' + this.props.post.id} - provider={this.props.provider} - link={link} - /> - ); + getOEmbedProvider(link) { + for (let i = 0; i < OEmbedProviders.length; i++) { + for (let j = 0; j < OEmbedProviders[i].patterns.length; j++) { + if (link.match(OEmbedProviders[i].patterns[j])) { + return OEmbedProviders[i]; + } + } + } + + return null; } - getComponent() { - switch (this.props.post.type) { - case 'slack_attachment': + render() { + if (this.props.post.type === 'slack_attachment') { return this.getSlackAttachment(); - case 'oEmbed': - return this.getOembedAttachment(); - default: - return ''; } - } - render() { - let content = []; + const link = Utils.extractLinks(this.props.post.message)[0]; + if (!link) { + return null; + } - if (this.props.post.type) { - const component = this.getComponent(); + if (Utils.isFeatureEnabled(Constants.PRE_RELEASE_FEATURES.EMBED_PREVIEW)) { + const provider = this.getOEmbedProvider(link); - if (component) { - content = component; + if (provider) { + return ( + <PostAttachmentOEmbed + provider={provider} + link={link} + /> + ); } } - return ( - <div> - {content} - </div> - ); + if (YoutubeVideo.isYoutubeLink(link)) { + return ( + <YoutubeVideo + channelId={this.props.post.channel_id} + link={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 ( + <PostImage + channelId={this.props.post.channel_id} + link={link} + /> + ); + } + } + + return null; } } PostBodyAdditionalContent.propTypes = { - post: React.PropTypes.object.isRequired, - provider: React.PropTypes.object + post: React.PropTypes.object.isRequired }; diff --git a/web/react/components/post_image.jsx b/web/react/components/post_image.jsx new file mode 100644 index 000000000..da4a25794 --- /dev/null +++ b/web/react/components/post_image.jsx @@ -0,0 +1,81 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +export default class PostImageEmbed extends React.Component { + constructor(props) { + super(props); + + this.handleLoadComplete = this.handleLoadComplete.bind(this); + this.handleLoadError = this.handleLoadError.bind(this); + + this.state = { + loaded: false, + errored: false + }; + } + + componentWillMount() { + this.loadImg(this.props.link); + } + + componentWillReceiveProps(nextProps) { + if (nextProps.link !== this.props.link) { + this.setState({ + loaded: false, + errored: false + }); + } + } + + componentDidUpdate(prevProps) { + if (!this.state.loaded && prevProps.link !== this.props.link) { + this.loadImg(this.props.link); + } + } + + loadImg(src) { + const img = new Image(); + img.onload = this.handleLoadComplete; + img.onerror = this.handleLoadError; + img.src = src; + } + + handleLoadComplete() { + this.setState({ + loaded: true + }); + } + + handleLoadError() { + this.setState({ + errored: true, + loaded: true + }); + } + + render() { + if (this.state.errored) { + return null; + } + + if (!this.state.loaded) { + return ( + <img + className='img-div placeholder' + height='500px' + /> + ); + } + + return ( + <img + className='img-div' + src={this.props.link} + /> + ); + } +} + +PostImageEmbed.propTypes = { + link: React.PropTypes.string.isRequired +}; diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx index 3a84b51f0..1ea7711ea 100644 --- a/web/react/components/posts_view.jsx +++ b/web/react/components/posts_view.jsx @@ -145,7 +145,7 @@ export default class PostsView extends React.Component { const postCtls = []; let previousPostDay = new Date(0); const userId = UserStore.getCurrentId(); - const profiles = this.props.profiles; + const profiles = this.props.profiles || {}; let renderedLastViewed = false; diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx index 975a39171..d007dd47f 100644 --- a/web/react/components/rhs_thread.jsx +++ b/web/react/components/rhs_thread.jsx @@ -109,6 +109,7 @@ export default class RhsThread extends React.Component { render() { const posts = this.state.posts; const selected = this.state.selected; + const profiles = this.state.profiles || {}; if (posts == null || selected == null) { return ( @@ -162,7 +163,7 @@ export default class RhsThread extends React.Component { if (UserStore.getCurrentId() === selected.user_id) { profile = UserStore.getCurrentUser(); } else { - profile = this.state.profiles[selected.user_id]; + profile = profiles[selected.user_id]; } return ( @@ -187,7 +188,7 @@ export default class RhsThread extends React.Component { if (UserStore.getCurrentId() === selected.user_id) { p = UserStore.getCurrentUser(); } else { - p = this.state.profiles[selected.user_id]; + p = profiles[selected.user_id]; } return ( <Comment diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index d10c5be27..55ece2c97 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -40,12 +40,14 @@ export default class SearchResults extends React.Component { this.mounted = false; this.onChange = this.onChange.bind(this); + this.onUserChange = this.onUserChange.bind(this); this.resize = this.resize.bind(this); this.handleResize = this.handleResize.bind(this); const state = getStateFromStores(); state.windowWidth = Utils.windowWidth(); state.windowHeight = Utils.windowHeight(); + state.profiles = JSON.parse(JSON.stringify(UserStore.getProfiles())); this.state = state; } @@ -53,6 +55,7 @@ export default class SearchResults extends React.Component { this.mounted = true; SearchStore.addSearchChangeListener(this.onChange); ChannelStore.addChangeListener(this.onChange); + UserStore.addChangeListener(this.onUserChange); this.resize(); window.addEventListener('resize', this.handleResize); } @@ -68,6 +71,7 @@ export default class SearchResults extends React.Component { componentWillUnmount() { SearchStore.removeSearchChangeListener(this.onChange); ChannelStore.removeChangeListener(this.onChange); + UserStore.removeChangeListener(this.onUserChange); this.mounted = false; window.removeEventListener('resize', this.handleResize); } @@ -85,6 +89,10 @@ export default class SearchResults extends React.Component { } } + onUserChange() { + this.setState({profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))}); + } + resize() { $('#search-items-container').scrollTop(0); if (this.state.windowWidth > 768) { @@ -101,6 +109,7 @@ export default class SearchResults extends React.Component { } var noResults = (!results || !results.order || !results.order.length); var searchTerm = SearchStore.getSearchTerm(); + const profiles = this.state.profiles || {}; var ctls = null; @@ -140,6 +149,7 @@ export default class SearchResults extends React.Component { key={post.id} channel={this.state.channels.get(post.channel_id)} post={post} + user={profiles[post.user_id]} term={searchTerm} isMentionSearch={this.props.isMentionSearch} /> diff --git a/web/react/components/search_results_item.jsx b/web/react/components/search_results_item.jsx index 3363c97f7..05292b7b3 100644 --- a/web/react/components/search_results_item.jsx +++ b/web/react/components/search_results_item.jsx @@ -36,9 +36,10 @@ export default class SearchResultsItem extends React.Component { } render() { - var channelName = null; + let channelName = null; const channel = this.props.channel; - var timestamp = UserStore.getCurrentUser().update_at; + const timestamp = UserStore.getCurrentUser().update_at; + const user = this.props.user || {}; if (channel) { channelName = channel.display_name; @@ -84,7 +85,7 @@ export default class SearchResultsItem extends React.Component { </div> <div> <ul className='post__header'> - <li className='col__name'><strong><UserProfile userId={this.props.post.user_id}/></strong></li> + <li className='col__name'><strong><UserProfile user={user}/></strong></li> <li className='col'> <time className='search-item-time'> <FormattedDate @@ -135,6 +136,7 @@ export default class SearchResultsItem extends React.Component { SearchResultsItem.propTypes = { post: React.PropTypes.object, + user: React.PropTypes.object, channel: React.PropTypes.object, isMentionSearch: React.PropTypes.bool, term: React.PropTypes.string |