diff options
Diffstat (limited to 'web')
-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 | ||||
-rw-r--r-- | web/react/utils/utils.jsx | 181 | ||||
-rw-r--r-- | web/sass-files/sass/partials/_command-box.scss | 17 |
11 files changed, 187 insertions, 201 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'> diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 61dcae6d8..be11c2e40 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -255,163 +255,6 @@ export function escapeRegExp(string) { return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); } -function handleYoutubeTime(link) { - var timeRegex = /[\\?&]t=([0-9hms]+)/; - - var time = link.trim().match(timeRegex); - if (!time || !time[1]) { - return ''; - } - - var hours = time[1].match(/([0-9]+)h/); - var minutes = time[1].match(/([0-9]+)m/); - var seconds = time[1].match(/([0-9]+)s/); - - var 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(); -} - -function getYoutubeEmbed(link) { - var regex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/; - - var youtubeId = link.trim().match(regex)[1]; - var time = 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; - } - var metadata = data.items[0].snippet; - $('.video-type.' + youtubeId).html('Youtube - '); - $('.video-uploader.' + youtubeId).html(metadata.channelTitle); - $('.video-title.' + youtubeId).find('a').html(metadata.title); - } - - if (global.window.config.GoogleDeveloperKey) { - $.ajax({ - async: true, - url: 'https://www.googleapis.com/youtube/v3/videos', - type: 'GET', - data: {part: 'snippet', id: youtubeId, key: global.window.config.GoogleDeveloperKey}, - success: success - }); - } - - return ( - <div className='post-comment'> - <h4> - <span className={'video-type ' + youtubeId}>YouTube</span> - <span className={'video-title ' + youtubeId}><a href={link}></a></span> - </h4> - <h4 className={'video-uploader ' + youtubeId}></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> - ); -} - -export function getEmbed(link) { - var ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/; - - var match = link.trim().match(ytRegex); - if (match && match[1].length === 11) { - return getYoutubeEmbed(link); - } - - // Generl embed feature turned off for now - return ''; - - // NEEDS REFACTORING WHEN TURNED BACK ON - /* - var id = parseInt((Math.random() * 1000000) + 1); - - $.ajax({ - type: 'GET', - url: 'https://query.yahooapis.com/v1/public/yql', - data: { - q: 'select * from html where url="' + link + "\" and xpath='html/head'", - format: 'json' - }, - async: true - }).done(function(data) { - if(!data.query.results) { - return; - } - - var headerData = data.query.results.head; - - var description = '' - for(var i = 0; i < headerData.meta.length; i++) { - if(headerData.meta[i].name && (headerData.meta[i].name === 'description' || headerData.meta[i].name === 'Description')){ - description = headerData.meta[i].content; - break; - } - } - - $('.embed-title.'+id).html(headerData.title); - $('.embed-description.'+id).html(description); - }) - - return ( - <div className='post-comment'> - <div className={'web-embed-data'}> - <p className={'embed-title ' + id} /> - <p className={'embed-description ' + id} /> - <p className={'embed-link ' + id}>{link}</p> - </div> - </div> - ); - */ -} - export function areStatesEqual(state1, state2) { return JSON.stringify(state1) === JSON.stringify(state2); } @@ -606,7 +449,7 @@ export function applyTheme(theme) { } if (theme.centerChannelBg) { - changeCss('.app__content, .markdown__table, .markdown__table tbody tr', 'background:' + theme.centerChannelBg, 1); + changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .command-box', 'background:' + theme.centerChannelBg, 1); changeCss('#post-list .post-list-holder-by-time', 'background:' + theme.centerChannelBg, 1); changeCss('#post-create', 'background:' + theme.centerChannelBg, 1); changeCss('.search-bar__container .search__form .search-bar', 'background:' + theme.centerChannelBg, 1); @@ -616,14 +459,18 @@ export function applyTheme(theme) { } if (theme.centerChannelColor) { - changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .loading-screen .loading__content .round', 'color:' + theme.centerChannelColor, 1); + changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .loading-screen .loading__content .round, .command-name', 'color:' + theme.centerChannelColor, 1); changeCss('#post-create', 'color:' + theme.centerChannelColor, 2); + changeCss('.mentions--top, .command-box', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 3); + changeCss('.mentions--top, .command-box', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 2); + changeCss('.mentions--top, .command-box', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 1); changeCss('.post-body hr', 'background:' + theme.centerChannelColor, 1); changeCss('.channel-header .heading', 'color:' + theme.centerChannelColor, 1); changeCss('.markdown__table tbody tr:nth-child(2n)', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.channel-header__info>div.dropdown .header-dropdown__icon', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1); changeCss('.channel-header #member_popover', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1); - changeCss('.custom-textarea, .custom-textarea:focus, .preview-container .preview-div, .post-image__column .post-image__details, .sidebar--right .sidebar-right__body, .markdown__table th, .markdown__table td', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); + changeCss('.custom-textarea, .custom-textarea:focus, .preview-container .preview-div, .post-image__column .post-image__details, .sidebar--right .sidebar-right__body, .markdown__table th, .markdown__table td, .command-box', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); + changeCss('.command-name', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); changeCss('.custom-textarea', 'color:' + theme.centerChannelColor, 1); changeCss('.post-image__column', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 2); changeCss('.post-image__column .post-image__details', 'color:' + theme.centerChannelColor, 2); @@ -641,7 +488,7 @@ export function applyTheme(theme) { changeCss('@media(max-width: 1800px){.inner__wrap.move--left .post.post--comment.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); changeCss('.post:hover, .sidebar--right .sidebar--right__header', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.date-separator.hovered--before:after, .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); - changeCss('.date-separator.hovered--after:before, .new-separator.hovered--after:before', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); + changeCss('.date-separator.hovered--after:before, .new-separator.hovered--after:before, .command-name:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.post.current--user:hover .post-body ', 'background: none;', 1); changeCss('.sidebar--right', 'color:' + theme.centerChannelColor, 2); } @@ -790,16 +637,12 @@ export function isValidUsername(name) { return error; } -export function updateTabTitle(name) { - document.title = name + ' ' + document.title.substring(document.title.lastIndexOf('-')); -} - export function updateAddressBar(channelName) { var teamURL = window.location.href.split('/channels')[0]; history.replaceState('data', '', teamURL + '/channels/' + channelName); } -export function switchChannel(channel, teammateName) { +export function switchChannel(channel) { AppDispatcher.handleViewAction({ type: ActionTypes.CLICK_CHANNEL, name: channel.name, @@ -808,12 +651,6 @@ export function switchChannel(channel, teammateName) { updateAddressBar(channel.name); - if (channel.type === 'D' && teammateName) { - updateTabTitle(teammateName); - } else { - updateTabTitle(channel.display_name); - } - AsyncClient.getChannels(true, true, true); AsyncClient.getChannelExtraInfo(true); AsyncClient.getPosts(channel.id); diff --git a/web/sass-files/sass/partials/_command-box.scss b/web/sass-files/sass/partials/_command-box.scss index 44eb9b8df..f1aa4dca2 100644 --- a/web/sass-files/sass/partials/_command-box.scss +++ b/web/sass-files/sass/partials/_command-box.scss @@ -4,20 +4,29 @@ width: 100%; border: $border-gray; bottom: 38px; + overflow: auto; @extend %popover-box-shadow; + .sidebar--right & { + bottom: 100px; + } } .command-name { position: relative; width: 100%; - background-color: #fff; - height: 37px; - line-height: 37px; - padding: 2px 10px 2px 5px; + line-height: 24px; + padding: 5px 10px 8px; z-index: 101; + font-size: 0.95em; + border-bottom: 1px solid #ddd; &:hover { background-color: #e8eaed; } + .command__desc { + margin-left: 5px; + @include opacity(0.5); + line-height: normal; + } } .command-desc { |