diff options
Diffstat (limited to 'web/react/components')
-rw-r--r-- | web/react/components/command_list.jsx | 2 | ||||
-rw-r--r-- | web/react/components/more_direct_channels.jsx | 2 | ||||
-rw-r--r-- | web/react/components/post_body.jsx | 122 | ||||
-rw-r--r-- | web/react/components/post_list.jsx | 14 | ||||
-rw-r--r-- | web/react/components/rename_channel_modal.jsx | 1 | ||||
-rw-r--r-- | web/react/components/search_results_item.jsx | 7 | ||||
-rw-r--r-- | web/react/components/sidebar.jsx | 15 | ||||
-rw-r--r-- | web/react/components/sidebar_right.jsx | 25 | ||||
-rw-r--r-- | web/react/components/user_settings/import_theme_modal.jsx | 2 |
9 files changed, 165 insertions, 25 deletions
diff --git a/web/react/components/command_list.jsx b/web/react/components/command_list.jsx index 63bd57c2a..fea7085b7 100644 --- a/web/react/components/command_list.jsx +++ b/web/react/components/command_list.jsx @@ -96,4 +96,4 @@ CommandList.defaultProps = { CommandList.propTypes = { addCommand: React.PropTypes.func, channelId: React.PropTypes.string -};
\ No newline at end of file +}; diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx index b7bce9b34..54d77c358 100644 --- a/web/react/components/more_direct_channels.jsx +++ b/web/react/components/more_direct_channels.jsx @@ -43,7 +43,7 @@ export default class MoreDirectChannels extends React.Component { handleClick = function clickHandler(e) { e.preventDefault(); - utils.switchChannel(channel, channel.teammate_username); + utils.switchChannel(channel); $(React.findDOMNode(self.refs.modal)).modal('hide'); }; } else { diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 6e98e4aba..48b268700 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -12,7 +12,10 @@ export default class PostBody extends React.Component { constructor(props) { super(props); + this.receivedYoutubeData = false; + this.parseEmojis = this.parseEmojis.bind(this); + this.createYoutubeEmbed = this.createYoutubeEmbed.bind(this); const linkData = Utils.extractLinks(this.props.post.message); this.state = {links: linkData.links, message: linkData.text}; @@ -50,6 +53,123 @@ export default class PostBody extends React.Component { this.setState({links: linkData.links, message: linkData.text}); } + handleYoutubeTime(link) { + const timeRegex = /[\\?&]t=([0-9hms]+)/; + + const time = link.trim().match(timeRegex); + if (!time || !time[1]) { + return ''; + } + + const hours = time[1].match(/([0-9]+)h/); + const minutes = time[1].match(/([0-9]+)m/); + const seconds = time[1].match(/([0-9]+)s/); + + let ticks = 0; + + if (hours && hours[1]) { + ticks += parseInt(hours[1], 10) * 3600; + } + + if (minutes && minutes[1]) { + ticks += parseInt(minutes[1], 10) * 60; + } + + if (seconds && seconds[1]) { + ticks += parseInt(seconds[1], 10); + } + + return '&start=' + ticks.toString(); + } + + createYoutubeEmbed(link) { + const ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/; + + const match = link.trim().match(ytRegex); + if (!match || match[1].length !== 11) { + return null; + } + + const youtubeId = match[1]; + const time = this.handleYoutubeTime(link); + + function onClick(e) { + var div = $(e.target).closest('.video-thumbnail__container')[0]; + var iframe = document.createElement('iframe'); + iframe.setAttribute('src', + 'https://www.youtube.com/embed/' + + div.id + + '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1' + + time); + iframe.setAttribute('width', '480px'); + iframe.setAttribute('height', '360px'); + iframe.setAttribute('type', 'text/html'); + iframe.setAttribute('frameborder', '0'); + iframe.setAttribute('allowfullscreen', 'allowfullscreen'); + + div.parentNode.replaceChild(iframe, div); + } + + function success(data) { + if (!data.items.length || !data.items[0].snippet) { + return null; + } + var metadata = data.items[0].snippet; + this.receivedYoutubeData = true; + this.setState({youtubeUploader: metadata.channelTitle, youtubeTitle: metadata.title}); + } + + if (global.window.config.GoogleDeveloperKey && !this.receivedYoutubeData) { + $.ajax({ + async: true, + url: 'https://www.googleapis.com/youtube/v3/videos', + type: 'GET', + data: {part: 'snippet', id: youtubeId, key: global.window.config.GoogleDeveloperKey}, + success + }); + } + + let header = 'Youtube'; + if (this.state.youtubeTitle) { + header = header + ' - '; + } + + let uploader = this.state.youtubeUploader; + if (!uploader) { + uploader = 'unknown'; + } + + return ( + <div className='post-comment'> + <h4> + <span className='video-type'>{header}</span> + <span className='video-title'><a href={link}>{this.state.youtubeTitle}</a></span> + </h4> + <h4 className='video-uploader'>{uploader}</h4> + <div + className='video-div embed-responsive-item' + id={youtubeId} + onClick={onClick} + > + <div className='embed-responsive embed-responsive-4by3 video-div__placeholder'> + <div + id={youtubeId} + className='video-thumbnail__container' + > + <img + className='video-thumbnail' + src={'https://i.ytimg.com/vi/' + youtubeId + '/hqdefault.jpg'} + /> + <div className='block'> + <span className='play-button'><span/></span> + </div> + </div> + </div> + </div> + </div> + ); + } + render() { const post = this.props.post; const filenames = this.props.post.filenames; @@ -133,7 +253,7 @@ export default class PostBody extends React.Component { let embed; if (filenames.length === 0 && this.state.links) { - embed = Utils.getEmbed(this.state.links[0]); + embed = this.createYoutubeEmbed(this.state.links[0]); } let fileAttachmentHolder = ''; diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 3e1e075bb..a31967257 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -128,6 +128,15 @@ export default class PostList extends React.Component { this.userHasSeenNew = true; } this.isUserScroll = true; + + $('.top-visible-post').removeClass('top-visible-post'); + + $(React.findDOMNode(this.refs.postlistcontent)).children().each(function select() { + if ($(this).position().top + $(this).height() / 2 > 0) { + $(this).addClass('top-visible-post'); + return false; + } + }); }); $('.post-list__content div .post').removeClass('post--last'); @@ -665,7 +674,10 @@ export default class PostList extends React.Component { className={'post-list-holder-by-time ' + activeClass} > <div className='post-list__table'> - <div className='post-list__content'> + <div + ref='postlistcontent' + className='post-list__content' + > {moreMessages} {postCtls} </div> diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx index 37958b649..9d514c741 100644 --- a/web/react/components/rename_channel_modal.jsx +++ b/web/react/components/rename_channel_modal.jsx @@ -78,7 +78,6 @@ export default class RenameChannelModal extends React.Component { $(React.findDOMNode(this.refs.modal)).modal('hide'); AsyncClient.getChannel(channel.id); - Utils.updateTabTitle(channel.display_name); Utils.updateAddressBar(channel.name); React.findDOMNode(this.refs.displayName).value = ''; diff --git a/web/react/components/search_results_item.jsx b/web/react/components/search_results_item.jsx index 32b521560..bdefdbee8 100644 --- a/web/react/components/search_results_item.jsx +++ b/web/react/components/search_results_item.jsx @@ -47,13 +47,8 @@ export default class SearchResultsItem extends React.Component { ); var postChannel = ChannelStore.get(this.props.post.channel_id); - var teammate = ''; - if (postChannel.type === 'D') { - teammate = utils.getDirectTeammate(this.props.post.channel_id).username; - } - - utils.switchChannel(postChannel, teammate); + utils.switchChannel(postChannel); } render() { diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 14664ed4d..6033f200f 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -268,14 +268,19 @@ export default class Sidebar extends React.Component { } } updateTitle() { - var channel = ChannelStore.getCurrent(); + const channel = ChannelStore.getCurrent(); if (channel) { + let currentSiteName = ''; + if (global.window.config.SiteName != null) { + currentSiteName = global.window.config.SiteName; + } + + let currentChannelName = channel.display_name; if (channel.type === 'D') { - var teammateUsername = Utils.getDirectTeammate(channel.id).username; - document.title = teammateUsername + ' ' + document.title.substring(document.title.lastIndexOf('-')); - } else { - document.title = channel.display_name + ' ' + document.title.substring(document.title.lastIndexOf('-')); + currentChannelName = Utils.getDirectTeammate(channel.id).username; } + + document.title = currentChannelName + ' - ' + this.props.teamDisplayName + ' ' + currentSiteName; } } onScroll() { diff --git a/web/react/components/sidebar_right.jsx b/web/react/components/sidebar_right.jsx index 913715154..e63418ae8 100644 --- a/web/react/components/sidebar_right.jsx +++ b/web/react/components/sidebar_right.jsx @@ -14,9 +14,10 @@ export default class SidebarRight extends React.Component { constructor(props) { super(props); + this.plScrolledToBottom = true; + this.onSelectedChange = this.onSelectedChange.bind(this); this.onSearchChange = this.onSearchChange.bind(this); - this.resize = this.resize.bind(this); this.state = getStateFromStores(); } @@ -28,6 +29,14 @@ export default class SidebarRight extends React.Component { PostStore.removeSearchChangeListener(this.onSearchChange); PostStore.removeSelectedPostChangeListener(this.onSelectedChange); } + componentDidUpdate() { + if (!this.plScrolledToBottom) { + $('.top-visible-post')[0].scrollIntoView(); + } else { + var postHolder = $('.post-list-holder-by-time'); + postHolder.scrollTop(postHolder[0].scrollHeight); + } + } onSelectedChange(fromSearch) { var newState = getStateFromStores(fromSearch); newState.from_search = fromSearch; @@ -41,15 +50,15 @@ export default class SidebarRight extends React.Component { this.setState(newState); } } - resize() { - var postHolder = $('.post-list-holder-by-time'); - postHolder[0].scrollTop = postHolder[0].scrollHeight - 224; - } render() { + var postHolder = $('.post-list-holder-by-time'); + const position = postHolder.scrollTop() + postHolder.height() + 14; + const bottom = postHolder[0].scrollHeight; + this.plScrolledToBottom = position >= bottom; + if (!(this.state.search_visible || this.state.post_right_visible)) { $('.inner__wrap').removeClass('move--left').removeClass('move--right'); $('.sidebar--right').removeClass('move--left'); - this.resize(); return ( <div></div> ); @@ -59,8 +68,8 @@ export default class SidebarRight extends React.Component { $('.sidebar--left').removeClass('move--right'); $('.sidebar--right').addClass('move--left'); $('.sidebar--right').prepend('<div class="sidebar__overlay"></div>'); - this.resize(); - setTimeout(function overlayTimer() { + + setTimeout(() => { $('.sidebar__overlay').fadeOut('200', function fadeOverlay() { $(this).remove(); }); diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx index 48be83afe..0f5cb59f5 100644 --- a/web/react/components/user_settings/import_theme_modal.jsx +++ b/web/react/components/user_settings/import_theme_modal.jsx @@ -140,7 +140,7 @@ export default class ImportThemeModal extends React.Component { > <Modal.Body> <p> - {'To import a theme, go to a Slack team and look for “”Preferences” -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:'} + {'To import a theme, go to a Slack team and look for “Preferences -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:'} </p> <div className='form-group less'> <div className='col-sm-9'> |