diff options
24 files changed, 443 insertions, 203 deletions
diff --git a/web/react/components/admin_console/email_settings.jsx b/web/react/components/admin_console/email_settings.jsx index c028d605d..12480e7eb 100644 --- a/web/react/components/admin_console/email_settings.jsx +++ b/web/react/components/admin_console/email_settings.jsx @@ -447,7 +447,7 @@ export default class EmailSettings extends React.Component { </div> <div className='help-text'> <button - className='btn' + className='btn btn-default' onClick={this.handleTestConnection} disabled={!this.state.sendEmailNotifications} id='connection-button' @@ -482,7 +482,7 @@ export default class EmailSettings extends React.Component { <p className='help-text'>{'32-character salt added to signing of email invites. Randomly generated on install. Click "Re-Generate" to create new salt.'}</p> <div className='help-text'> <button - className='btn' + className='btn btn-default' onClick={this.handleGenerateInvite} disabled={!this.state.sendEmailNotifications} > @@ -513,7 +513,7 @@ export default class EmailSettings extends React.Component { <p className='help-text'>{'32-character salt added to signing of password reset emails. Randomly generated on install. Click "Re-Generate" to create new salt.'}</p> <div className='help-text'> <button - className='btn' + className='btn btn-default' onClick={this.handleGenerateReset} disabled={!this.state.sendEmailNotifications} > diff --git a/web/react/components/admin_console/gitlab_settings.jsx b/web/react/components/admin_console/gitlab_settings.jsx index 5c22bf5cf..41c8ad2fc 100644 --- a/web/react/components/admin_console/gitlab_settings.jsx +++ b/web/react/components/admin_console/gitlab_settings.jsx @@ -116,12 +116,14 @@ export default class GitLabSettings extends React.Component { <p className='help-text'> {'When true, Mattermost allows team creation and account signup using GitLab OAuth.'} <br/> </p> - <ol className='help-text'> - <li>{'Log in to your GitLab account and go to Applications -> Profile Settings.'}</li> - <li>{'Enter Redirect URIs "<your-mattermost-url>/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "<your-mattermost-url>/signup/gitlab/complete". '}</li> - <li>{'Then use "Secret" and "Id" fields from GitLab to complete the options below.'}</li> - <li>{'Complete the Endpoint URLs below. '}</li> - </ol> + <div className='help-text'> + <ol> + <li>{'Log in to your GitLab account and go to Applications -> Profile Settings.'}</li> + <li>{'Enter Redirect URIs "<your-mattermost-url>/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "<your-mattermost-url>/signup/gitlab/complete". '}</li> + <li>{'Then use "Secret" and "Id" fields from GitLab to complete the options below.'}</li> + <li>{'Complete the Endpoint URLs below. '}</li> + </ol> + </div> </div> </div> diff --git a/web/react/components/admin_console/sql_settings.jsx b/web/react/components/admin_console/sql_settings.jsx index 16a69e664..655daa30c 100644 --- a/web/react/components/admin_console/sql_settings.jsx +++ b/web/react/components/admin_console/sql_settings.jsx @@ -220,7 +220,7 @@ export default class SqlSettings extends React.Component { <p className='help-text'>{'32-character salt available to encrypt and decrypt sensitive fields in database.'}</p> <div className='help-text'> <button - className='help-link' + className='btn btn-default' onClick={this.handleGenerate} > {'Re-Generate'} diff --git a/web/react/components/channel_loader.jsx b/web/react/components/channel_loader.jsx index d0d6ab5e2..270631db2 100644 --- a/web/react/components/channel_loader.jsx +++ b/web/react/components/channel_loader.jsx @@ -75,15 +75,6 @@ export default class ChannelLoader extends React.Component { Utils.applyTheme(Constants.THEMES.default); } - /* Setup global mouse events */ - $('body').on('click', function hidePopover(e) { - $('[data-toggle="popover"]').each(function eachPopover() { - if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) { - $(this).popover('hide'); - } - }); - }); - $('body').on('mouseenter mouseleave', '.post', function mouseOver(ev) { if (ev.type === 'mouseenter') { $(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--after'); diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 2ac5d2179..fa3f82c2e 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -46,7 +46,9 @@ export default class CreateComment extends React.Component { componentDidUpdate(prevProps, prevState) { if (prevState.uploadsInProgress < this.state.uploadsInProgress) { $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight); - $('.post-right__scroll').perfectScrollbar('update'); + if ($(window).width() > 768) { + $('.post-right__scroll').perfectScrollbar('update'); + } } } handleSubmit(e) { diff --git a/web/react/components/email_verify.jsx b/web/react/components/email_verify.jsx index 9be7f97f8..940b01f8d 100644 --- a/web/react/components/email_verify.jsx +++ b/web/react/components/email_verify.jsx @@ -39,11 +39,9 @@ export default class EmailVerify extends React.Component { return ( <div className='col-sm-12'> - <div className='panel panel-default verify_panel'> - <div className='panel-heading'> - <h3 className='panel-title'>{title}</h3> - </div> - <div className='panel-body'> + <div className='signup-team__container'> + <h3>{title}</h3> + <div> {body} {resend} {resendConfirm} diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx index bc610cd60..99babe714 100644 --- a/web/react/components/more_direct_channels.jsx +++ b/web/react/components/more_direct_channels.jsx @@ -1,133 +1,273 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -var TeamStore = require('../stores/team_store.jsx'); -var Client = require('../utils/client.jsx'); -var Constants = require('../utils/constants.jsx'); -var AsyncClient = require('../utils/async_client.jsx'); -var PreferenceStore = require('../stores/preference_store.jsx'); -var utils = require('../utils/utils.jsx'); +const AsyncClient = require('../utils/async_client.jsx'); +const ChannelStore = require('../stores/channel_store.jsx'); +const Constants = require('../utils/constants.jsx'); +const Client = require('../utils/client.jsx'); +const Modal = ReactBootstrap.Modal; +const PreferenceStore = require('../stores/preference_store.jsx'); +const TeamStore = require('../stores/team_store.jsx'); +const UserStore = require('../stores/user_store.jsx'); +const Utils = require('../utils/utils.jsx'); export default class MoreDirectChannels extends React.Component { constructor(props) { super(props); - this.state = {channels: [], loadingDMChannel: -1}; + this.handleFilterChange = this.handleFilterChange.bind(this); + this.handleHide = this.handleHide.bind(this); + this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this); + this.handleUserChange = this.handleUserChange.bind(this); + + this.createRowForUser = this.createRowForUser.bind(this); + + this.state = { + users: this.getUsersFromStore(), + filter: '', + loadingDMChannel: -1 + }; + } + + getUsersFromStore() { + const currentId = UserStore.getCurrentId(); + const profiles = UserStore.getProfiles(); + const users = []; + + for (const id in profiles) { + if (id !== currentId) { + users.push(profiles[id]); + } + } + + users.sort((a, b) => a.username.localeCompare(b.username)); + + return users; } componentDidMount() { - $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => { - var button = e.relatedTarget; - this.setState({channels: $(button).data('channels')}); // eslint-disable-line react/no-did-mount-set-state - }); + UserStore.addChangeListener(this.handleUserChange); } - handleJoinDirectChannel(channel) { - const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, channel.teammate_id, 'true'); - AsyncClient.savePreferences([preference]); + componentWillUnmount() { + UserStore.addChangeListener(this.handleUserChange); } - render() { - var directMessageItems = this.state.channels.map((channel, index) => { - var badge = ''; - var titleClass = ''; - var handleClick = null; - - if (channel.fake) { - // It's a direct message channel that doesn't exist yet so let's create it now - var otherUserId = utils.getUserIdFromChannelName(channel); - - if (this.state.loadingDMChannel === index) { - badge = ( - <img - className='channel-loading-gif pull-right' - src='/static/images/load.gif' - /> - ); - } + handleFilterChange() { + const filter = React.findDOMNode(this.refs.filter).value; - if (this.state.loadingDMChannel === -1) { - handleClick = (e) => { - e.preventDefault(); - this.setState({loadingDMChannel: index}); - this.handleJoinDirectChannel(channel); - - Client.createDirectChannel(channel, otherUserId, - (data) => { - $(React.findDOMNode(this.refs.modal)).modal('hide'); - this.setState({loadingDMChannel: -1}); - AsyncClient.getChannel(data.id); - utils.switchChannel(data); - }, - () => { - this.setState({loadingDMChannel: -1}); - window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name; - } - ); - }; - } - } else { - if (channel.unread) { - badge = <span className='badge pull-right small'>{channel.unread}</span>; - titleClass = 'unread-title'; + if (filter !== this.state.filter) { + this.setState({filter}); + } + } + + handleHide() { + if (this.props.onModalDismissed) { + this.props.onModalDismissed(); + } + + this.setState({filter: ''}); + } + + handleShowDirectChannel(teammate, e) { + if (this.state.loadingDMChannel !== -1) { + return; + } + + e.preventDefault(); + + const channelName = Utils.getDirectChannelName(UserStore.getCurrentId(), teammate.id); + let channel = ChannelStore.getByName(channelName); + + const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, teammate.id, 'true'); + AsyncClient.savePreferences([preference]); + + if (channel) { + Utils.switchChannel(channel); + + this.handleHide(); + } else { + this.setState({loadingDMChannel: teammate.id}); + + channel = { + name: channelName, + last_post_at: 0, + total_msg_count: 0, + type: 'D', + display_name: teammate.username, + teammate_id: teammate.id, + status: UserStore.getStatus(teammate.id) + }; + + Client.createDirectChannel( + channel, + teammate.id, + (data) => { + this.setState({loadingDMChannel: -1}); + + AsyncClient.getChannel(data.id); + Utils.switchChannel(data); + + this.handleHide(); + }, + () => { + this.setState({loadingDMChannel: -1}); + window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channelName; } + ); + } + } - handleClick = (e) => { - e.preventDefault(); - this.handleJoinDirectChannel(channel); - utils.switchChannel(channel); - $(React.findDOMNode(this.refs.modal)).modal('hide'); - }; - } + handleUserChange() { + this.setState({users: this.getUsersFromStore()}); + } + + createRowForUser(user) { + const details = []; + + const fullName = Utils.getFullName(user); + if (fullName) { + details.push( + <span + key={`${user.id}__full-name`} + className='full-name' + > + {fullName} + </span> + ); + } - return ( - <li key={channel.name}> - <a - className={'sidebar-channel ' + titleClass} - href='#' - onClick={handleClick} - >{badge}{channel.display_name}</a> - </li> + if (user.nickname) { + const separator = fullName ? ' - ' : ''; + details.push( + <span + key={`${user.nickname}__nickname`} + className='nickname' + > + {separator + user.nickname} + </span> ); - }); + } + + let joinButton; + if (this.state.loadingDMChannel === user.id) { + joinButton = ( + <img + className='channel-loading-gif' + src='/static/images/load.gif' + /> + ); + } else { + joinButton = ( + <button + type='button' + className='btn btn-primary btn-message' + onClick={this.handleShowDirectChannel.bind(this, user)} + > + {'Message'} + </button> + ); + } return ( - <div - className='modal fade' - id='more_direct_channels' - ref='modal' - tabIndex='-1' - role='dialog' - aria-hidden='true' + <li + key={user.id} + className='direct-channel' > - <div className='modal-dialog'> - <div className='modal-content'> - <div className='modal-header'> - <button - type='button' - className='close' - data-dismiss='modal' - > - <span aria-hidden='true'>{'×'}</span> - <span className='sr-only'>{'Close'}</span> - </button> - <h4 className='modal-title'>{'More Direct Messages'}</h4> - </div> - <div className='modal-body'> - <ul className='nav nav-pills nav-stacked'> - {directMessageItems} - </ul> - </div> - <div className='modal-footer'> - <button - type='button' - className='btn btn-default' - data-dismiss='modal' - >{'Close'}</button> - </div> + <div className='col-xs-1 image-div'> + <img + className='profile-image' + src={`/api/v1/users/${user.id}/image?time=${user.update_at}`} + /> + </div> + <div className='col-xs-9'> + <div className='username'> + {user.username} </div> + <div> + {details} + </div> + </div> + <div className='col-xs-2 btn-div'> + {joinButton} </div> - </div> + </li> + ); + } + + render() { + if (!this.props.show) { + return null; + } + + let users = this.state.users; + if (this.state.filter !== '') { + users = users.filter((user) => { + return user.username.indexOf(this.state.filter) !== -1 || + user.first_name.indexOf(this.state.filter) !== -1 || + user.last_name.indexOf(this.state.filter) !== -1 || + user.nickname.indexOf(this.state.filter) !== -1; + }); + } + + const userEntries = users.map(this.createRowForUser); + + if (userEntries.length === 0) { + userEntries.push(<li key='no-users-found'>{'No users found :('}</li>); + } + + let memberString = 'Member'; + if (users.length !== 1) { + memberString += 's'; + } + + let count; + if (users.length === this.state.users.length) { + count = `${users.length} ${memberString}`; + } else { + count = `${users.length} ${memberString} of ${this.state.users.length} Total`; + } + + return ( + <Modal + className='modal-direct-channels' + show={this.props.show} + bsSize='large' + onHide={this.handleHide} + > + <Modal.Header closeButton={true}> + <Modal.Title>{'More Direct Messages'}</Modal.Title> + </Modal.Header> + <Modal.Body> + <div> + <input + ref='filter' + className='form-control filter-textbox' + placeholder='Search members' + onInput={this.handleFilterChange} + style={{width: '200px', display: 'inline'}} + /> + <span className='member-count pull-right'>{count}</span> + </div> + <ul className='user-list'> + {userEntries} + </ul> + </Modal.Body> + <Modal.Footer> + <button + type='button' + className='btn btn-default' + onClick={this.handleHide} + > + {'Close'} + </button> + </Modal.Footer> + </Modal> ); } } + +MoreDirectChannels.propTypes = { + show: React.PropTypes.bool.isRequired, + onModalDismissed: React.PropTypes.func +}; diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx index 5d9052fd7..16ae693fa 100644 --- a/web/react/components/popover_list_members.jsx +++ b/web/react/components/popover_list_members.jsx @@ -2,6 +2,8 @@ // See License.txt for license information. var UserStore = require('../stores/user_store.jsx'); +var Popover = ReactBootstrap.Popover; +var OverlayTrigger = ReactBootstrap.OverlayTrigger; export default class PopoverListMembers extends React.Component { componentDidMount() { @@ -24,16 +26,9 @@ export default class PopoverListMembers extends React.Component { }); } }; - - $('#member_popover').popover({placement: 'bottom', trigger: 'click', html: true}); - $('body').on('click', function onClick(e) { - if (e.target.parentNode && $(e.target.parentNode.parentNode)[0] !== $('#member_popover')[0] && $(e.target).parents('.popover.in').length === 0) { - $('#member_popover').popover('hide'); - } - }); } render() { - let popoverHtml = ''; + let popoverHtml = []; let count = 0; let countText = '-'; const members = this.props.members; @@ -46,7 +41,7 @@ export default class PopoverListMembers extends React.Component { members.forEach(function addMemberElement(m) { if (teamMembers[m.username] && teamMembers[m.username].delete_at <= 0) { - popoverHtml += `<div class='text--nowrap'>${m.username}</div>`; + popoverHtml.push(<div className='text--nowrap'>{m.username}</div>); count++; } }); @@ -59,15 +54,14 @@ export default class PopoverListMembers extends React.Component { } return ( - <div - id='member_popover' - data-toggle='popover' - data-content={popoverHtml} - data-original-title='Members' + <OverlayTrigger + trigger='click' + placement='bottom' + rootClose='true' + overlay={<Popover title='Members'>{popoverHtml}</Popover>} > - <div - id='member_tooltip' - > + <div id='member_popover'> + <div> {countText} <span className='fa fa-user' @@ -75,6 +69,7 @@ export default class PopoverListMembers extends React.Component { /> </div> </div> + </OverlayTrigger> ); } } diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx index 41fd74adb..131253aa5 100644 --- a/web/react/components/rhs_thread.jsx +++ b/web/react/components/rhs_thread.jsx @@ -52,8 +52,6 @@ export default class RhsThread extends React.Component { if ($('.post-right__scroll')[0]) { $('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight); } - - $('.post-right__scroll').perfectScrollbar('update'); this.resize(); } componentWillUnmount() { @@ -100,8 +98,10 @@ export default class RhsThread extends React.Component { var height = $(window).height() - $('#error_bar').outerHeight() - 100; $('.post-right__scroll').css('height', height + 'px'); $('.post-right__scroll').scrollTop(100000); - $('.post-right__scroll').perfectScrollbar(); - $('.post-right__scroll').perfectScrollbar('update'); + if ($(window).width() > 768) { + $('.post-right__scroll').perfectScrollbar(); + $('.post-right__scroll').perfectScrollbar('update'); + } } render() { var postList = this.state.postList; diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index 5eea3c501..e55fd3752 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -56,7 +56,9 @@ export default class SearchResults extends React.Component { var height = $(window).height() - $('#error_bar').outerHeight() - 100; $('#search-items-container').css('height', height + 'px'); $('#search-items-container').scrollTop(0); - $('#search-items-container').perfectScrollbar(); + if ($(window).width() > 768) { + $('#search-items-container').perfectScrollbar(); + } } render() { diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 4911f17ef..e7d90bb88 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -8,6 +8,7 @@ const Client = require('../utils/client.jsx'); const Constants = require('../utils/constants.jsx'); const PreferenceStore = require('../stores/preference_store.jsx'); const NewChannelFlow = require('./new_channel_flow.jsx'); +const MoreDirectChannels = require('./more_direct_channels.jsx'); const SearchBox = require('./search_bar.jsx'); const SidebarHeader = require('./sidebar_header.jsx'); const SocketStore = require('../stores/socket_store.jsx'); @@ -33,12 +34,19 @@ export default class Sidebar extends React.Component { this.onResize = this.onResize.bind(this); this.updateUnreadIndicators = this.updateUnreadIndicators.bind(this); this.handleLeaveDirectChannel = this.handleLeaveDirectChannel.bind(this); + + this.showNewChannelModal = this.showNewChannelModal.bind(this); + this.hideNewChannelModal = this.hideNewChannelModal.bind(this); + this.showMoreDirectChannelsModal = this.showMoreDirectChannelsModal.bind(this); + this.hideMoreDirectChannelsModal = this.hideMoreDirectChannelsModal.bind(this); + this.createChannelElement = this.createChannelElement.bind(this); this.isLeaving = new Map(); const state = this.getStateFromStores(); - state.modal = ''; + state.newChannelModalType = ''; + state.showMoreDirectChannelsModal = false; state.loadingDMChannel = -1; this.state = state; @@ -47,10 +55,11 @@ export default class Sidebar extends React.Component { const members = ChannelStore.getAllMembers(); var teamMemberMap = UserStore.getActiveOnlyProfiles(); var currentId = ChannelStore.getCurrentId(); + const currentUserId = UserStore.getCurrentId(); var teammates = []; for (var id in teamMemberMap) { - if (id === UserStore.getCurrentId()) { + if (id === currentUserId) { continue; } teammates.push(teamMemberMap[id]); @@ -58,22 +67,16 @@ export default class Sidebar extends React.Component { const preferences = PreferenceStore.getPreferences(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW); - // Create lists of all read and unread direct channels var visibleDirectChannels = []; - var hiddenDirectChannels = []; + var hiddenDirectChannelCount = 0; for (var i = 0; i < teammates.length; i++) { const teammate = teammates[i]; - if (teammate.id === UserStore.getCurrentId()) { + if (teammate.id === currentUserId) { continue; } - var channelName = ''; - if (teammate.id > UserStore.getCurrentId()) { - channelName = UserStore.getCurrentId() + '__' + teammate.id; - } else { - channelName = teammate.id + '__' + UserStore.getCurrentId(); - } + const channelName = Utils.getDirectChannelName(currentUserId, teammate.id); let forceShow = false; let channel = ChannelStore.getByName(channelName); @@ -106,19 +109,18 @@ export default class Sidebar extends React.Component { visibleDirectChannels.push(channel); } else { - hiddenDirectChannels.push(channel); + hiddenDirectChannelCount += 1; } } visibleDirectChannels.sort(this.sortChannelsByDisplayName); - hiddenDirectChannels.sort(this.sortChannelsByDisplayName); return { activeId: currentId, channels: ChannelStore.getAll(), members, visibleDirectChannels, - hiddenDirectChannels + hiddenDirectChannelCount }; } @@ -132,7 +134,9 @@ export default class Sidebar extends React.Component { AsyncClient.getDirectChannelPreferences(); - $('.nav-pills__container').perfectScrollbar(); + if ($(window).width() > 768) { + $('.nav-pills__container').perfectScrollbar(); + } this.updateTitle(); this.updateUnreadIndicators(); @@ -336,6 +340,20 @@ export default class Sidebar extends React.Component { return a.display_name.localeCompare(b.display_name); } + showNewChannelModal(type) { + this.setState({newChannelModalType: type}); + } + hideNewChannelModal() { + this.setState({newChannelModalType: ''}); + } + + showMoreDirectChannelsModal() { + this.setState({showDirectChannelsModal: true}); + } + hideMoreDirectChannelsModal() { + this.setState({showDirectChannelsModal: false}); + } + createChannelElement(channel, index, arr, handleClose) { var members = this.state.members; var activeId = this.state.activeId; @@ -532,25 +550,21 @@ export default class Sidebar extends React.Component { head.appendChild(link); var directMessageMore = null; - if (this.state.hiddenDirectChannels.length > 0) { + if (this.state.hiddenDirectChannelCount > 0) { directMessageMore = ( <li key='more'> <a - key={`more${this.state.hiddenDirectChannels.length}`} href='#' - data-toggle='modal' - className='nav-more' - data-target='#more_direct_channels' - data-channels={JSON.stringify(this.state.hiddenDirectChannels)} + onClick={this.showMoreDirectChannelsModal} > - {'More (' + this.state.hiddenDirectChannels.length + ')'} + {'More (' + this.state.hiddenDirectChannelCount + ')'} </a> </li> ); } let showChannelModal = false; - if (this.state.modal !== '') { + if (this.state.newChannelModalType !== '') { showChannelModal = true; } @@ -561,9 +575,14 @@ export default class Sidebar extends React.Component { <div> <NewChannelFlow show={showChannelModal} - channelType={this.state.modal} - onModalDismissed={() => this.setState({modal: ''})} + channelType={this.state.newChannelModalType} + onModalDismissed={this.hideNewChannelModal} + /> + <MoreDirectChannels + show={this.state.showDirectChannelsModal} + onModalDismissed={this.hideMoreDirectChannelsModal} /> + <SidebarHeader teamDisplayName={this.props.teamDisplayName} teamName={this.props.teamName} @@ -599,7 +618,7 @@ export default class Sidebar extends React.Component { <a className='add-channel-btn' href='#' - onClick={() => this.setState({modal: 'O'})} + onClick={this.showNewChannelModal.bind(this, 'O')} > {'+'} </a> @@ -632,7 +651,7 @@ export default class Sidebar extends React.Component { <a className='add-channel-btn' href='#' - onClick={() => this.setState({modal: 'P'})} + onClick={this.showNewChannelModal.bind(this, 'P')} > {'+'} </a> diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx index cc6165c1b..715161b4f 100644 --- a/web/react/components/user_profile.jsx +++ b/web/react/components/user_profile.jsx @@ -3,6 +3,8 @@ var Utils = require('../utils/utils.jsx'); var UserStore = require('../stores/user_store.jsx'); +var Popover = ReactBootstrap.Popover; +var OverlayTrigger = ReactBootstrap.OverlayTrigger; var id = 0; @@ -32,7 +34,6 @@ export default class UserProfile extends React.Component { componentDidMount() { UserStore.addChangeListener(this.onChange); if (!this.props.disablePopover) { - $('#profile_' + this.uniqueId).popover({placement: 'right', container: 'body', trigger: 'click hover', html: true, delay: {show: 200, hide: 100}}); $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'}); } } @@ -62,23 +63,46 @@ export default class UserProfile extends React.Component { return <div>{name}</div>; } - var dataContent = '<img class="user-popover__image" src="/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at + '" height="128" width="128" />'; + var dataContent = []; + dataContent.push( + <img className='user-popover__image' + src={'/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at} + height='128' + width='128' + /> + ); if (!global.window.config.ShowEmailAddress === 'true') { - dataContent += '<div class="text-nowrap">Email not shared</div>'; + dataContent.push(<div className='text-nowrap'>{'Email not shared'}</div>); } else { - dataContent += '<div data-toggle="tooltip" title="' + this.state.profile.email + '"><a href="mailto:' + this.state.profile.email + '" class="text-nowrap text-lowercase user-popover__email">' + this.state.profile.email + '</a></div>'; + dataContent.push( + <div + data-toggle='tooltip' + title="' + this.state.profile.email + '" + > + <a + href="mailto:' + this.state.profile.email + '" + className='text-nowrap text-lowercase user-popover__email' + > + {this.state.profile.email} + </a> + </div> + ); } return ( + <OverlayTrigger + trigger='click' + placement='right' + rootClose='true' + overlay={<Popover title={this.state.profile.username}>{dataContent}</Popover>} + > <div className='user-popover' id={'profile_' + this.uniqueId} - data-toggle='popover' - data-content={dataContent} - data-original-title={this.state.profile.username} > {name} </div> + </OverlayTrigger> ); } } diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index 4d6d3fd15..698213eef 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -24,7 +24,6 @@ var TeamSettingsModal = require('../components/team_settings_modal.jsx'); var ChannelMembersModal = require('../components/channel_members.jsx'); var ChannelInviteModal = require('../components/channel_invite_modal.jsx'); var TeamMembersModal = require('../components/team_members.jsx'); -var DirectChannelModal = require('../components/more_direct_channels.jsx'); var ErrorBar = require('../components/error_bar.jsx'); var ErrorStore = require('../stores/error_store.jsx'); var ChannelLoader = require('../components/channel_loader.jsx'); @@ -156,11 +155,6 @@ function setupChannelPage(props) { ); React.render( - <DirectChannelModal />, - document.getElementById('direct_channel_modal') - ); - - React.render( <PostListContainer />, document.getElementById('post-list') ); diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 8f697a9c5..0457d620f 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -467,6 +467,10 @@ export function applyTheme(theme) { changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1); changeCss('.post-image__column .post-image__details', 'background:' + theme.centerChannelBg, 1); changeCss('.sidebar--right, .dropdown-menu, .popover', 'background:' + theme.centerChannelBg, 1); + changeCss('.popover.bottom>.arrow:after', 'border-bottom-color:' + theme.centerChannelBg, 1); + changeCss('.popover.right>.arrow:after', 'border-right-color:' + theme.centerChannelBg, 1); + changeCss('.popover.left>.arrow:after', 'border-left-color:' + theme.centerChannelBg, 1); + changeCss('.popover.top>.arrow:after', 'border-top-color:' + theme.centerChannelBg, 1); changeCss('.search-bar__container .search__form .search-bar, .form-control', 'background:' + theme.centerChannelBg, 1); } @@ -911,6 +915,18 @@ export function isBrowserEdge() { return window.naviagtor && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('edge') > -1; } +export function getDirectChannelName(id, otherId) { + let handle; + + if (otherId > id) { + handle = id + '__' + otherId; + } else { + handle = otherId + '__' + id; + } + + return handle; +} + // Used to get the id of the other user from a DM channel export function getUserIdFromChannelName(channel) { var ids = channel.name.split('__'); diff --git a/web/sass-files/sass/partials/_admin-console.scss b/web/sass-files/sass/partials/_admin-console.scss index 4b4fc1664..14f1d9c2f 100644 --- a/web/sass-files/sass/partials/_admin-console.scss +++ b/web/sass-files/sass/partials/_admin-console.scss @@ -15,12 +15,11 @@ .nav { li { padding: 0; + @include opacity(1); .icon { width: 17px; } > a { - padding: 9px 15px; - display: block; &:hover, &.active, &:focus { background-color: #EAEAEA; } @@ -117,8 +116,10 @@ .form-group { margin-bottom: 25px; } - ul, ol { - padding-left: 23px; + .help-text { + ul, ol { + padding-left: 23px; + } } .help-text { margin: 10px 0 0 15px; diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss index 44a474213..cb5ff67b5 100644 --- a/web/sass-files/sass/partials/_base.scss +++ b/web/sass-files/sass/partials/_base.scss @@ -39,10 +39,6 @@ img { height: auto; } -.input-group-addon { - background: transparent; -} - .popover { color: #333; &.bottom, &.right, &.top, &.left { @@ -53,6 +49,9 @@ img { .popover-title { background: rgba(black, 0.05); } + .popover-content { + white-space: pre-wrap; + } } .dropdown-menu { @@ -182,6 +181,10 @@ a:focus, a:hover { background-color: white !important; } +.alert { + padding: 8px 12px; +} + .emoji { width: 1.5em; height: 1.5em; diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss index 6dbb82810..01057423d 100644 --- a/web/sass-files/sass/partials/_files.scss +++ b/web/sass-files/sass/partials/_files.scss @@ -194,6 +194,7 @@ vertical-align: top; .file-details__name { + color: #333; font-size: 16px; } .file-details__info { diff --git a/web/sass-files/sass/partials/_markdown.scss b/web/sass-files/sass/partials/_markdown.scss index 1aa942ad0..70c99f504 100644 --- a/web/sass-files/sass/partials/_markdown.scss +++ b/web/sass-files/sass/partials/_markdown.scss @@ -1,5 +1,6 @@ .markdown__heading { font-weight: 700; + line-height: 1.5; } .markdown__paragraph-inline { display: inline; diff --git a/web/sass-files/sass/partials/_modal.scss b/web/sass-files/sass/partials/_modal.scss index 90ea8ce2c..b942a5a40 100644 --- a/web/sass-files/sass/partials/_modal.scss +++ b/web/sass-files/sass/partials/_modal.scss @@ -329,3 +329,49 @@ } } } + +.modal-direct-channels { + .user-list { + list-style-type: none; + margin: 15px 0px 0px; + max-height: 600px; + padding: 0px; + overflow: auto; + + li { + border-bottom: 1px solid #ddd; + height: 60px; + padding: 10px 0px; + + .image-div { + padding: 0px; + + .profile-image { + width: 40px; + height: 40px; + @include border-radius(20px); + } + } + + .username { + font-weight: bold; + } + + .nickname { + color: #888; + } + + .btn-div { + padding: 0px; + .btn-message { + position: relative; + top: 5px; + } + } + + &:last-child { + border-bottom: 0px; + } + } + } +} diff --git a/web/sass-files/sass/partials/_post_right.scss b/web/sass-files/sass/partials/_post_right.scss index b72176a11..9557d7570 100644 --- a/web/sass-files/sass/partials/_post_right.scss +++ b/web/sass-files/sass/partials/_post_right.scss @@ -93,6 +93,7 @@ .post-right__scroll { position: relative; + overflow: auto; } .post-right-comment-time { diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss index dbe6029ec..09f2c179e 100644 --- a/web/sass-files/sass/partials/_responsive.scss +++ b/web/sass-files/sass/partials/_responsive.scss @@ -586,11 +586,14 @@ &.move--right { @include translate3d(0, 0, 0); } + .nav-pills__unread-indicator-bottom { + bottom: 10px; + } .badge { top: 13px; } > div { - padding-bottom: 105px; + padding-bottom: 65px; } .team__header { display: none; diff --git a/web/sass-files/sass/partials/_search.scss b/web/sass-files/sass/partials/_search.scss index 2de1b5380..ca90ec46d 100644 --- a/web/sass-files/sass/partials/_search.scss +++ b/web/sass-files/sass/partials/_search.scss @@ -67,6 +67,7 @@ } .search-items-container { + overflow: auto; position: relative; } diff --git a/web/sass-files/sass/partials/_sidebar--left.scss b/web/sass-files/sass/partials/_sidebar--left.scss index 24aaef059..831c19cff 100644 --- a/web/sass-files/sass/partials/_sidebar--left.scss +++ b/web/sass-files/sass/partials/_sidebar--left.scss @@ -69,7 +69,7 @@ top: 66px; } .nav-pills__unread-indicator-bottom { - bottom: 10px; + bottom: 20px; } .nav { diff --git a/web/templates/channel.html b/web/templates/channel.html index 13fd80d75..4b8318d43 100644 --- a/web/templates/channel.html +++ b/web/templates/channel.html @@ -54,7 +54,7 @@ <script> window.setup_channel_page({{ .Props }}); $('body').tooltip( {selector: '[data-toggle=tooltip]'} ); - $('.modal-body').css('max-height', $(window).height() - 150); + $('.modal-body').css('max-height', $(window).height() - 200); $('.modal-body').perfectScrollbar(); </script> </body> |