diff options
Diffstat (limited to 'web')
25 files changed, 321 insertions, 248 deletions
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index a8d4ec100..79c7f90a9 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -276,26 +276,27 @@ export default class ChannelHeader extends React.Component { </li> ); - if (!ChannelStore.isDefault(channel)) { - if (isAdmin) { - dropdownContents.push( - <li - key='rename_channel' - role='presentation' + if (isAdmin) { + dropdownContents.push( + <li + key='rename_channel' + role='presentation' + > + <a + role='menuitem' + href='#' + data-toggle='modal' + data-target='#rename_channel' + data-display={channel.display_name} + data-name={channel.name} + data-channelid={channel.id} > - <a - role='menuitem' - href='#' - data-toggle='modal' - data-target='#rename_channel' - data-display={channel.display_name} - data-name={channel.name} - data-channelid={channel.id} - > - {'Rename '}{channelTerm}{'...'} - </a> - </li> - ); + {'Rename '}{channelTerm}{'...'} + </a> + </li> + ); + + if (!ChannelStore.isDefault(channel)) { dropdownContents.push( <li key='delete_channel' @@ -314,7 +315,9 @@ export default class ChannelHeader extends React.Component { </li> ); } + } + if (!ChannelStore.isDefault(channel)) { dropdownContents.push( <li key='leave_channel' diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx index af29f219e..7ad1f9305 100644 --- a/web/react/components/navbar.jsx +++ b/web/react/components/navbar.jsx @@ -178,18 +178,35 @@ export default class Navbar extends React.Component { var manageMembersOption; var renameChannelOption; var deleteChannelOption; - if (!isDirect && isAdmin && !ChannelStore.isDefault(channel)) { - manageMembersOption = ( - <li role='presentation'> - <a - role='menuitem' - href='#' - onClick={() => this.setState({showMembersModal: true})} - > - {'Manage Members'} - </a> - </li> - ); + if (!isDirect && isAdmin) { + if (!ChannelStore.isDefault(channel)) { + manageMembersOption = ( + <li role='presentation'> + <a + role='menuitem' + href='#' + onClick={() => this.setState({showMembersModal: true})} + > + {'Manage Members'} + </a> + </li> + ); + + deleteChannelOption = ( + <li role='presentation'> + <a + role='menuitem' + href='#' + data-toggle='modal' + data-target='#delete_channel' + data-title={channel.display_name} + data-channelid={channel.id} + > + {'Delete Channel...'} + </a> + </li> + ); + } renameChannelOption = ( <li role='presentation'> @@ -206,21 +223,6 @@ export default class Navbar extends React.Component { </a> </li> ); - - deleteChannelOption = ( - <li role='presentation'> - <a - role='menuitem' - href='#' - data-toggle='modal' - data-target='#delete_channel' - data-title={channel.display_name} - data-channelid={channel.id} - > - {'Delete Channel...'} - </a> - </li> - ); } var notificationPreferenceOption; diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 617b4b36c..975ac64dc 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -16,13 +16,13 @@ export default class PostBody extends React.Component { 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); @@ -117,8 +117,12 @@ export default class PostBody extends React.Component { return embed; } - if (link.substring(link.length - 4) === '.gif') { - return this.createGifEmbed(link, this.state.gifLoaded); + 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; @@ -135,29 +139,29 @@ export default class PostBody extends React.Component { return false; } - 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.embed = this.createGifEmbed(src, true); - this.setState({gifLoaded: true}); + this.embed = this.createImageEmbed(src, true); + this.setState({imgLoaded: true}); } ); - gif.src = src; + img.src = src; } - createGifEmbed(link, isLoaded) { + createImageEmbed(link, isLoaded) { if (!isLoaded) { - this.loadGif(link); + this.loadImg(link); return ( <img - className='gif-div placeholder' + className='img-div placeholder' height='500px' /> ); @@ -165,7 +169,7 @@ export default class PostBody extends React.Component { return ( <img - className='gif-div' + className='img-div' src={link} /> ); diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx index 9fb3af035..f47009cce 100644 --- a/web/react/components/rename_channel_modal.jsx +++ b/web/react/components/rename_channel_modal.jsx @@ -5,6 +5,7 @@ const Utils = require('../utils/utils.jsx'); const Client = require('../utils/client.jsx'); const AsyncClient = require('../utils/async_client.jsx'); const ChannelStore = require('../stores/channel_store.jsx'); +const Constants = require('../utils/constants.jsx'); export default class RenameChannelModal extends React.Component { constructor(props) { @@ -36,10 +37,10 @@ export default class RenameChannelModal extends React.Component { return; } - let channel = ChannelStore.get(this.state.channelId); + const channel = ChannelStore.get(this.state.channelId); const oldName = channel.name; const oldDisplayName = channel.displayName; - let state = {serverError: ''}; + const state = {serverError: ''}; channel.display_name = this.state.displayName.trim(); if (!channel.display_name) { @@ -60,7 +61,7 @@ export default class RenameChannelModal extends React.Component { state.nameError = 'This field must be less than 22 characters'; state.invalid = true; } else { - let cleanedName = Utils.cleanUpUrlable(channel.name); + const cleanedName = Utils.cleanUpUrlable(channel.name); if (cleanedName === channel.name) { state.nameError = ''; } else { @@ -76,7 +77,7 @@ export default class RenameChannelModal extends React.Component { } Client.updateChannel(channel, - function handleUpdateSuccess() { + () => { $(ReactDOM.findDOMNode(this.refs.modal)).modal('hide'); AsyncClient.getChannel(channel.id); @@ -84,12 +85,12 @@ export default class RenameChannelModal extends React.Component { ReactDOM.findDOMNode(this.refs.displayName).value = ''; ReactDOM.findDOMNode(this.refs.channelName).value = ''; - }.bind(this), - function handleUpdateError(err) { + }, + (err) => { state.serverError = err.message; state.invalid = true; this.setState(state); - }.bind(this) + } ); } onNameChange() { @@ -99,10 +100,12 @@ export default class RenameChannelModal extends React.Component { this.setState({displayName: ReactDOM.findDOMNode(this.refs.displayName).value}); } displayNameKeyUp() { - const displayName = ReactDOM.findDOMNode(this.refs.displayName).value.trim(); - const channelName = Utils.cleanUpUrlable(displayName); - ReactDOM.findDOMNode(this.refs.channelName).value = channelName; - this.setState({channelName: channelName}); + if (this.state.channelName !== Constants.DEFAULT_CHANNEL) { + const displayName = ReactDOM.findDOMNode(this.refs.displayName).value.trim(); + const channelName = Utils.cleanUpUrlable(displayName); + ReactDOM.findDOMNode(this.refs.channelName).value = channelName; + this.setState({channelName: channelName}); + } } handleClose() { this.setState({ @@ -150,6 +153,15 @@ export default class RenameChannelModal extends React.Component { serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>; } + let handleInputLabel = 'Handle'; + let handleInputClass = 'form-control'; + let readOnlyHandleInput = false; + if (this.state.channelName === Constants.DEFAULT_CHANNEL) { + handleInputLabel += ' - Cannot be changed for the default channel'; + handleInputClass += ' disabled-input'; + readOnlyHandleInput = true; + } + return ( <div className='modal fade' @@ -167,15 +179,15 @@ export default class RenameChannelModal extends React.Component { className='close' data-dismiss='modal' > - <span aria-hidden='true'>×</span> - <span className='sr-only'>Close</span> + <span aria-hidden='true'>{'×'}</span> + <span className='sr-only'>{'Close'}</span> </button> - <h4 className='modal-title'>Rename Channel</h4> + <h4 className='modal-title'>{'Rename Channel'}</h4> </div> <form role='form'> <div className='modal-body'> <div className={displayNameClass}> - <label className='control-label'>Display Name</label> + <label className='control-label'>{'Display Name'}</label> <input onKeyUp={this.displayNameKeyUp} onChange={this.onDisplayNameChange} @@ -190,15 +202,16 @@ export default class RenameChannelModal extends React.Component { {displayNameError} </div> <div className={nameClass}> - <label className='control-label'>Handle</label> + <label className='control-label'>{handleInputLabel}</label> <input onChange={this.onNameChange} type='text' - className='form-control' + className={handleInputClass} ref='channelName' placeholder='lowercase alphanumeric's only' value={this.state.channelName} maxLength='64' + readOnly={readOnlyHandleInput} /> {nameError} </div> @@ -210,14 +223,14 @@ export default class RenameChannelModal extends React.Component { className='btn btn-default' data-dismiss='modal' > - Cancel + {'Cancel'} </button> <button onClick={this.handleSubmit} type='submit' className='btn btn-primary' > - Save + {'Save'} </button> </div> </form> diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx index 7c11de7cf..cc062c538 100644 --- a/web/react/components/rhs_thread.jsx +++ b/web/react/components/rhs_thread.jsx @@ -117,8 +117,6 @@ export default class RhsThread extends React.Component { } } resize() { - var height = this.state.windowHeight - $('#error_bar').outerHeight() - 100; - $('.post-right__scroll').css('height', height + 'px'); $('.post-right__scroll').scrollTop(100000); if (this.state.windowWidth > 768) { $('.post-right__scroll').perfectScrollbar(); diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index 2f0068908..f4d8647db 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -62,8 +62,6 @@ export default class SearchResults extends React.Component { } resize() { - var height = this.state.windowHeight - $('#error_bar').outerHeight() - 100; - $('#search-items-container').css('height', height + 'px'); $('#search-items-container').scrollTop(0); if (this.state.windowWidth > 768) { $('#search-items-container').perfectScrollbar(); diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx index 2135e3ef3..6a428e884 100644 --- a/web/react/components/sidebar_right_menu.jsx +++ b/web/react/components/sidebar_right_menu.jsx @@ -48,7 +48,7 @@ export default class SidebarRightMenu extends React.Component { href='#' onClick={InviteMemberModal.show} > - <i className='glyphicon glyphicon-user'></i>Invite New Member + <i className='fa fa-user'></i>Invite New Member </a> </li> ); @@ -61,7 +61,7 @@ export default class SidebarRightMenu extends React.Component { data-target='#get_link' data-title='Team Invite' data-value={utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + TeamStore.getCurrent().invite_id} - ><i className='glyphicon glyphicon-link'></i>Get Team Invite Link</a> + ><i className='fa fa-link'></i>Get Team Invite Link</a> </li> ); } @@ -74,7 +74,7 @@ export default class SidebarRightMenu extends React.Component { href='#' data-toggle='modal' data-target='#team_settings' - ><i className='glyphicon glyphicon-globe'></i>Team Settings</a> + ><i className='fa fa-globe'></i>Team Settings</a> </li> ); manageLink = ( @@ -84,7 +84,7 @@ export default class SidebarRightMenu extends React.Component { data-toggle='modal' data-target='#team_members' > - <i className='glyphicon glyphicon-wrench'></i>Manage Members</a> + <i className='fa fa-users'></i>Manage Members</a> </li> ); } @@ -95,7 +95,7 @@ export default class SidebarRightMenu extends React.Component { <a href={'/admin_console?' + utils.getSessionIndex()} > - <i className='glyphicon glyphicon-wrench'></i>System Console</a> + <i className='fa fa-wrench'></i>System Console</a> </li> ); } @@ -125,7 +125,7 @@ export default class SidebarRightMenu extends React.Component { href='#' onClick={() => this.setState({showUserSettingsModal: true})} > - <i className='glyphicon glyphicon-cog'></i>Account Settings + <i className='fa fa-cog'></i>Account Settings </a> </li> {teamSettingsLink} @@ -137,18 +137,18 @@ export default class SidebarRightMenu extends React.Component { <a href='#' onClick={this.handleLogoutClick} - ><i className='glyphicon glyphicon-log-out'></i>Logout</a></li> + ><i className='fa fa-sign-out'></i>Logout</a></li> <li className='divider'></li> <li> <a target='_blank' href='/static/help/configure_links.html' - ><i className='glyphicon glyphicon-question-sign'></i>Help</a></li> + ><i className='fa fa-question'></i>Help</a></li> <li> <a target='_blank' href='/static/help/configure_links.html' - ><i className='glyphicon glyphicon-earphone'></i>Report a Problem</a></li> + ><i className='fa fa-phone'></i>Report a Problem</a></li> </ul> </div> <UserSettingsModal diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx index 587ef5ec2..a50859489 100644 --- a/web/react/components/team_general_tab.jsx +++ b/web/react/components/team_general_tab.jsx @@ -393,7 +393,7 @@ export default class GeneralTab extends React.Component { </div> </div> </div> - <div className='setting-list__hint'>{'When allowing open invites this code is used as part of the signup process. Changing this code will invalidate the previous open signup link.'}</div> + <div className='setting-list__hint'>{'Your Invite Code is used in the URL sent to people to join your team. Regenerating your Invite Code will invalidate the URLs in previous invitations, unless "Allow anyone to sign-up from login page" is enabled.'}</div> </div> ); @@ -452,6 +452,7 @@ export default class GeneralTab extends React.Component { server_error={serverError} client_error={clientError} updateSection={this.onUpdateNameSection} + extraInfo='Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.' /> ); } else { diff --git a/web/react/components/tutorial/tutorial_tip.jsx b/web/react/components/tutorial/tutorial_tip.jsx index 75d73e920..dd231b816 100644 --- a/web/react/components/tutorial/tutorial_tip.jsx +++ b/web/react/components/tutorial/tutorial_tip.jsx @@ -51,21 +51,22 @@ export default class TutorialTip extends React.Component { const dots = []; if (this.props.screens.length > 1) { for (let i = 0; i < this.props.screens.length; i++) { + let className = 'circle'; if (i === this.state.currentScreen) { - dots.push( - <div - className='circle active' - key={'dotactive' + i} - /> - ); - } else { - dots.push( - <div - className='circle' - key={'dotinactive' + i} - /> - ); + className += ' active'; } + + dots.push( + <a + href='#' + key={'dotactive' + i} + className={className} + onClick={(e) => { //eslint-disable-line no-loop-func + e.preventDefault(); + this.setState({currentScreen: i}); + }} + /> + ); } } diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx index 1bfae6930..b363f0673 100644 --- a/web/react/components/user_settings/user_settings_general.jsx +++ b/web/react/components/user_settings/user_settings_general.jsx @@ -1,15 +1,16 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -var UserStore = require('../../stores/user_store.jsx'); -var ErrorStore = require('../../stores/error_store.jsx'); -var SettingItemMin = require('../setting_item_min.jsx'); -var SettingItemMax = require('../setting_item_max.jsx'); -var SettingPicture = require('../setting_picture.jsx'); -var client = require('../../utils/client.jsx'); -var AsyncClient = require('../../utils/async_client.jsx'); -var utils = require('../../utils/utils.jsx'); -var assign = require('object-assign'); +const SettingItemMin = require('../setting_item_min.jsx'); +const SettingItemMax = require('../setting_item_max.jsx'); +const SettingPicture = require('../setting_picture.jsx'); + +const UserStore = require('../../stores/user_store.jsx'); +const ErrorStore = require('../../stores/error_store.jsx'); + +const Client = require('../../utils/client.jsx'); +const AsyncClient = require('../../utils/async_client.jsx'); +const Utils = require('../../utils/utils.jsx'); export default class UserSettingsGeneralTab extends React.Component { constructor(props) { @@ -32,17 +33,15 @@ export default class UserSettingsGeneralTab extends React.Component { this.updatePicture = this.updatePicture.bind(this); this.updateSection = this.updateSection.bind(this); - this.setupInitialState = this.setupInitialState.bind(this); - this.state = this.setupInitialState(props); } submitUsername(e) { e.preventDefault(); - var user = this.props.user; - var username = this.state.username.trim().toLowerCase(); + const user = Object.assign({}, this.props.user); + const username = this.state.username.trim().toLowerCase(); - var usernameError = utils.isValidUsername(username); + const usernameError = Utils.isValidUsername(username); if (usernameError === 'Cannot use a reserved word as a username.') { this.setState({clientError: 'This username is reserved, please choose a new one.'}); return; @@ -52,7 +51,7 @@ export default class UserSettingsGeneralTab extends React.Component { } if (user.username === username) { - this.setState({clientError: 'You must submit a new username'}); + this.setState({clientError: 'You must submit a new username.', emailError: '', serverError: ''}); return; } @@ -63,11 +62,11 @@ export default class UserSettingsGeneralTab extends React.Component { submitNickname(e) { e.preventDefault(); - var user = UserStore.getCurrentUser(); - var nickname = this.state.nickname.trim(); + const user = Object.assign({}, this.props.user); + const nickname = this.state.nickname.trim(); if (user.nickname === nickname) { - this.setState({clientError: 'You must submit a new nickname'}); + this.setState({clientError: 'You must submit a new nickname.', emailError: '', serverError: ''}); return; } @@ -78,12 +77,12 @@ export default class UserSettingsGeneralTab extends React.Component { submitName(e) { e.preventDefault(); - var user = UserStore.getCurrentUser(); - var firstName = this.state.firstName.trim(); - var lastName = this.state.lastName.trim(); + const user = Object.assign({}, this.props.user); + const firstName = this.state.firstName.trim(); + const lastName = this.state.lastName.trim(); if (user.first_name === firstName && user.last_name === lastName) { - this.setState({clientError: 'You must submit a new first or last name'}); + this.setState({clientError: 'You must submit a new first or last name.', emailError: '', serverError: ''}); return; } @@ -95,21 +94,21 @@ export default class UserSettingsGeneralTab extends React.Component { submitEmail(e) { e.preventDefault(); - var user = UserStore.getCurrentUser(); - var email = this.state.email.trim().toLowerCase(); - var confirmEmail = this.state.confirmEmail.trim().toLowerCase(); + const user = Object.assign({}, this.props.user); + const email = this.state.email.trim().toLowerCase(); + const confirmEmail = this.state.confirmEmail.trim().toLowerCase(); if (user.email === email) { return; } - if (email === '' || !utils.isEmail(email)) { - this.setState({emailError: 'Please enter a valid email address'}); + if (email === '' || !Utils.isEmail(email)) { + this.setState({emailError: 'Please enter a valid email address.', clientError: '', serverError: ''}); return; } if (email !== confirmEmail) { - this.setState({emailError: 'The new emails you entered do not match'}); + this.setState({emailError: 'The new emails you entered do not match.', clientError: '', serverError: ''}); return; } @@ -117,7 +116,7 @@ export default class UserSettingsGeneralTab extends React.Component { this.submitUser(user, true); } submitUser(user, emailUpdated) { - client.updateUser(user, + Client.updateUser(user, () => { this.updateSection(''); AsyncClient.getMe(); @@ -130,13 +129,13 @@ export default class UserSettingsGeneralTab extends React.Component { } }, (err) => { - var state = this.setupInitialState(this.props); + let serverError; if (err.message) { - state.serverError = err.message; + serverError = err.message; } else { - state.serverError = err; + serverError = err; } - this.setState(state); + this.setState({serverError, emailError: '', clientError: ''}); } ); } @@ -151,10 +150,10 @@ export default class UserSettingsGeneralTab extends React.Component { return; } - var picture = this.state.picture; + const picture = this.state.picture; if (picture.type !== 'image/jpeg' && picture.type !== 'image/png') { - this.setState({clientError: 'Only JPG or PNG images may be used for profile pictures'}); + this.setState({clientError: 'Only JPG or PNG images may be used for profile pictures.'}); return; } @@ -162,17 +161,17 @@ export default class UserSettingsGeneralTab extends React.Component { formData.append('image', picture, picture.name); this.setState({loadingPicture: true}); - client.uploadProfileImage(formData, - function imageUploadSuccess() { + Client.uploadProfileImage(formData, + () => { this.submitActive = false; AsyncClient.getMe(); window.location.reload(); - }.bind(this), - function imageUploadFailure(err) { + }, + (err) => { var state = this.setupInitialState(this.props); state.serverError = err.message; this.setState(state); - }.bind(this) + } ); } updateUsername(e) { @@ -205,34 +204,34 @@ export default class UserSettingsGeneralTab extends React.Component { } updateSection(section) { const emailChangeInProgress = this.state.emailChangeInProgress; - this.setState(assign({}, this.setupInitialState(this.props), {emailChangeInProgress: emailChangeInProgress, clientError: '', serverError: '', emailError: ''})); + this.setState(Object.assign({}, this.setupInitialState(this.props), {emailChangeInProgress, clientError: '', serverError: '', emailError: ''})); this.submitActive = false; this.props.updateSection(section); } setupInitialState(props) { - var user = props.user; + const user = props.user; return {username: user.username, firstName: user.first_name, lastName: user.last_name, nickname: user.nickname, email: user.email, confirmEmail: '', picture: null, loadingPicture: false, emailChangeInProgress: false}; } render() { - var user = this.props.user; + const user = this.props.user; - var clientError = null; + let clientError = null; if (this.state.clientError) { clientError = this.state.clientError; } - var serverError = null; + let serverError = null; if (this.state.serverError) { serverError = this.state.serverError; } - var emailError = null; + let emailError = null; if (this.state.emailError) { emailError = this.state.emailError; } - var nameSection; - var inputs = []; + let nameSection; + const inputs = []; if (this.props.activeSection === 'name') { inputs.push( @@ -298,15 +297,15 @@ export default class UserSettingsGeneralTab extends React.Component { submit={this.submitName} server_error={serverError} client_error={clientError} - updateSection={function clearSection(e) { + updateSection={(e) => { this.updateSection(''); e.preventDefault(); - }.bind(this)} + }} extraInfo={extraInfo} /> ); } else { - var fullName = ''; + let fullName = ''; if (user.first_name && user.last_name) { fullName = user.first_name + ' ' + user.last_name; @@ -320,17 +319,17 @@ export default class UserSettingsGeneralTab extends React.Component { <SettingItemMin title='Full Name' describe={fullName} - updateSection={function updateNameSection() { + updateSection={() => { this.updateSection('name'); - }.bind(this)} + }} /> ); } - var nicknameSection; + let nicknameSection; if (this.props.activeSection === 'nickname') { let nicknameLabel = 'Nickname'; - if (utils.isMobile()) { + if (Utils.isMobile()) { nicknameLabel = ''; } @@ -364,10 +363,10 @@ export default class UserSettingsGeneralTab extends React.Component { submit={this.submitNickname} server_error={serverError} client_error={clientError} - updateSection={function clearSection(e) { + updateSection={(e) => { this.updateSection(''); e.preventDefault(); - }.bind(this)} + }} extraInfo={extraInfo} /> ); @@ -376,17 +375,17 @@ export default class UserSettingsGeneralTab extends React.Component { <SettingItemMin title='Nickname' describe={UserStore.getCurrentUser().nickname} - updateSection={function updateNicknameSection() { + updateSection={() => { this.updateSection('nickname'); - }.bind(this)} + }} /> ); } - var usernameSection; + let usernameSection; if (this.props.activeSection === 'username') { let usernameLabel = 'Username'; - if (utils.isMobile()) { + if (Utils.isMobile()) { usernameLabel = ''; } @@ -416,10 +415,10 @@ export default class UserSettingsGeneralTab extends React.Component { submit={this.submitUsername} server_error={serverError} client_error={clientError} - updateSection={function clearSection(e) { + updateSection={(e) => { this.updateSection(''); e.preventDefault(); - }.bind(this)} + }} extraInfo={extraInfo} /> ); @@ -428,13 +427,14 @@ export default class UserSettingsGeneralTab extends React.Component { <SettingItemMin title='Username' describe={UserStore.getCurrentUser().username} - updateSection={function updateUsernameSection() { + updateSection={() => { this.updateSection('username'); - }.bind(this)} + }} /> ); } - var emailSection; + + let emailSection; if (this.props.activeSection === 'email') { const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true'; const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true'; @@ -507,10 +507,10 @@ export default class UserSettingsGeneralTab extends React.Component { submit={submit} server_error={serverError} client_error={emailError} - updateSection={function clearSection(e) { + updateSection={(e) => { this.updateSection(''); e.preventDefault(); - }.bind(this)} + }} /> ); } else { @@ -534,26 +534,26 @@ export default class UserSettingsGeneralTab extends React.Component { <SettingItemMin title='Email' describe={describe} - updateSection={function updateEmailSection() { + updateSection={() => { this.updateSection('email'); - }.bind(this)} + }} /> ); } - var pictureSection; + let pictureSection; if (this.props.activeSection === 'picture') { pictureSection = ( <SettingPicture title='Profile Picture' submit={this.submitPicture} - src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update + '&' + utils.getSessionIndex()} + src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update + '&' + Utils.getSessionIndex()} server_error={serverError} client_error={clientError} - updateSection={function clearSection(e) { + updateSection={(e) => { this.updateSection(''); e.preventDefault(); - }.bind(this)} + }} picture={this.state.picture} pictureChange={this.updatePicture} submitActive={this.submitActive} @@ -561,17 +561,17 @@ export default class UserSettingsGeneralTab extends React.Component { /> ); } else { - var minMessage = 'Click \'Edit\' to upload an image.'; + let minMessage = 'Click \'Edit\' to upload an image.'; if (user.last_picture_update) { - minMessage = 'Image last updated ' + utils.displayDate(user.last_picture_update); + minMessage = 'Image last updated ' + Utils.displayDate(user.last_picture_update); } pictureSection = ( <SettingItemMin title='Profile Picture' describe={minMessage} - updateSection={function updatePictureSection() { + updateSection={() => { this.updateSection('picture'); - }.bind(this)} + }} /> ); } @@ -619,10 +619,10 @@ export default class UserSettingsGeneralTab extends React.Component { } UserSettingsGeneralTab.propTypes = { - user: React.PropTypes.object, - updateSection: React.PropTypes.func, - updateTab: React.PropTypes.func, - activeSection: React.PropTypes.string, + user: React.PropTypes.object.isRequired, + updateSection: React.PropTypes.func.isRequired, + updateTab: React.PropTypes.func.isRequired, + activeSection: React.PropTypes.string.isRequired, closeModal: React.PropTypes.func.isRequired, collapseModal: React.PropTypes.func.isRequired }; diff --git a/web/react/utils/channel_intro_mssages.jsx b/web/react/utils/channel_intro_mssages.jsx index f27e23a82..11b69e28f 100644 --- a/web/react/utils/channel_intro_mssages.jsx +++ b/web/react/utils/channel_intro_mssages.jsx @@ -92,6 +92,7 @@ export function createOffTopicIntroMessage(channel, showInviteModal) { </a> <a href='#' + className='intro-links' onClick={showInviteModal} > <i className='fa fa-user-plus'></i>{'Invite others to this channel'} diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 6f3924829..77b3ecb57 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -616,7 +616,7 @@ 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, .command-name, .modal .modal-content, .dropdown-menu, .popover, .mentions-name, .tip-overlay', 'color:' + theme.centerChannelColor, 1); changeCss('#post-create', 'color:' + theme.centerChannelColor, 2); - changeCss('.channel-header__links a', 'fill:' + changeOpacity(theme.centerChannelColor, 0.7), 1); + changeCss('.channel-header__links a', 'fill:' + changeOpacity(theme.centerChannelColor, 0.9), 1); changeCss('.channel-header__links a:hover, .channel-header__links a:active', 'fill:' + 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); @@ -644,7 +644,7 @@ export function applyTheme(theme) { changeCss('.search-bar__container .search__form .search-bar, .form-control', 'color:' + theme.centerChannelColor, 2); changeCss('@media(max-width: 768px){.search-bar__container .search__form .search-bar', 'background:' + changeOpacity(theme.centerChannelColor, 0.2) + '; color: inherit;', 1); changeCss('.input-group-addon, .search-bar__container .search__form, .form-control', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1); - changeCss('.form-control:focus', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); + changeCss('.form-control:focus, .channel-header__links', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); changeCss('.attachment .attachment__content', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.3), 1); changeCss('.channel-intro .channel-intro__content, .webhooks__container', 'background:' + changeOpacity(theme.centerChannelColor, 0.05), 1); changeCss('.date-separator .separator__text', 'color:' + theme.centerChannelColor, 2); @@ -656,7 +656,7 @@ export function applyTheme(theme) { changeCss('@media(max-width: 1440px){.post.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); changeCss('@media(max-width: 1440px){.post.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2); 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, .modal .more-table tbody>tr:hover td, .sidebar--right .sidebar--right__header, .settings-modal .settings-table .settings-content .section-min:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); + changeCss('.post:hover, .channel-header__links a:hover, .channel-header__links a:active, .modal .more-table tbody>tr:hover td, .settings-modal .settings-table .settings-content .section-min:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.date-separator.hovered--before:after, .date-separator.hovered--after:before, .new-separator.hovered--after:before, .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1); changeCss('.command-name:hover, .mentions-name:hover, .mentions-focus, .dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover, .bot-indicator', 'background:' + changeOpacity(theme.centerChannelColor, 0.15), 1); changeCss('code', 'background:' + changeOpacity(theme.centerChannelColor, 0.1), 1); diff --git a/web/sass-files/sass/partials/_content.scss b/web/sass-files/sass/partials/_content.scss index 6228cc45e..44a959a9b 100644 --- a/web/sass-files/sass/partials/_content.scss +++ b/web/sass-files/sass/partials/_content.scss @@ -20,7 +20,6 @@ background: #fff; @include display-flex; @include flex-direction(column); - flex-direction: column; .channel__wrap & { padding-top: 0; } diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss index 49fb8e847..168634b5e 100644 --- a/web/sass-files/sass/partials/_files.scss +++ b/web/sass-files/sass/partials/_files.scss @@ -1,9 +1,9 @@ .preview-container { position: relative; - margin-top: 10px; + margin-top: 25px; width: 100%; - max-height: 110px; - height: 110px; + max-height: 100px; + height: 100px; white-space: nowrap; overflow-x: auto; overflow-y: hidden; @@ -11,7 +11,7 @@ display: inline-block; width: 120px; height: 100px; - margin: 7px 0 0 5px; + margin: 0 0 0 5px; vertical-align: top; position: relative; border: 1px solid #DDD; diff --git a/web/sass-files/sass/partials/_forms.scss b/web/sass-files/sass/partials/_forms.scss index 2d7b6cd26..685677ad0 100644 --- a/web/sass-files/sass/partials/_forms.scss +++ b/web/sass-files/sass/partials/_forms.scss @@ -43,3 +43,7 @@ margin: 10px 0 0; color: #999; } + +.disabled-input { + background-color: #dddddd !important; +} diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss index 67c938b8c..69bc56841 100644 --- a/web/sass-files/sass/partials/_headers.scss +++ b/web/sass-files/sass/partials/_headers.scss @@ -1,8 +1,5 @@ #channel-header { - padding: 3px 0; - height: 58px; - flex: 0 0 58px; - @include flex(0 0 50px); + @include flex(0 0 56px); } .row { &.header { @@ -45,9 +42,9 @@ text-overflow: ellipsis; margin-top: 2px; max-height: 45px; - .markdown-inline-img { - max-height: 45px - } + .markdown-inline-img { + max-height: 45px + } } &.popover { white-space: normal; @@ -97,7 +94,7 @@ .sidebar--left, .sidebar--menu { .team__header { position: relative; - padding: 10px; + padding: 9px 10px; @include legacy-pie-clearfix; &:before { @include single-transition(all, 0.05s, linear); @@ -125,7 +122,7 @@ .navbar-right { font-size: 0.85em; position: absolute; - top: 11px; + top: 10px; right: 22px; z-index: 5; .dropdown-toggle { @@ -217,7 +214,7 @@ width:100%; border-left: none; font-size: 14px; - line-height: 50px; + line-height: 56px; #member_popover { margin-right: 5px; width: 45px; @@ -296,17 +293,19 @@ .channel-header__links { height: 32px; - vertical-align: top; - display: inline-block; - width: 15px; - margin: 9px 6px 3px 0; - a { + width: 32px; + @include border-radius(50px); + border: 1px solid #ccc; + margin-right: 10px; + > a { + @include border-radius(50px); height: 100%; display: block; + @include single-transition(all, 0.1s, ease-in); } svg { vertical-align: top; - margin-top: 8px; + margin: 7px 0 0 0px; fill: inherit; } } diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index 7b26581e9..5a7d79afe 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -332,18 +332,19 @@ body.ios { } .post-create-footer { @include clearfix; - padding: 0; + padding: 3px 0 0 0; + font-size: 13px; .has-error { .control-label { + height: 0; + display: block; font-weight: normal; margin-bottom: 0; } } .msg-typing { min-height: 25px; - line-height: 25px; display: block; - font-size: 13px; @include opacity(0.7); } } @@ -425,9 +426,6 @@ body.ios { } } } - .post-create-footer { - padding: 0; - } p { margin: 0 0 1em; line-height: 1.6em; @@ -553,13 +551,14 @@ body.ios { } &.post-info { .post-profile-time { - color: #a8adb7; vertical-align: top; + width: 150px; max-width: 220px; overflow: hidden; display: block; white-space: nowrap; text-overflow: ellipsis; + margin: 0 0 0 50px; } } .post-header-col { @@ -585,7 +584,7 @@ body.ios { } } .post-profile-time { - color: #a8adb7; + @include opacity(0.5); } } .post-comment { diff --git a/web/sass-files/sass/partials/_post_right.scss b/web/sass-files/sass/partials/_post_right.scss index ba41d3b95..54c3bcdf8 100644 --- a/web/sass-files/sass/partials/_post_right.scss +++ b/web/sass-files/sass/partials/_post_right.scss @@ -1,4 +1,7 @@ .post-right__container { + @include display-flex; + @include flex-direction(column); + height: 100%; .post-right-root-message { padding: 1em 1em 0; @@ -19,6 +22,15 @@ margin: 1em 0 0 0; } } + .post-header { + .post-profile-time { + width: 200px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } } .post-create__container { @@ -44,7 +56,7 @@ } .post-create-footer { width: 100%; - padding-top: 5px; + padding: 5px 0 10px; } .post-right-comments-upload-in-progress { padding: 6px 0; @@ -103,8 +115,9 @@ .post-right__scroll { position: relative; - overflow: scroll; + overflow: auto; -webkit-overflow-scrolling: touch; + @include flex(1 1 auto); } .post-right-comment-time { diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss index 3877bfaf0..aad991035 100644 --- a/web/sass-files/sass/partials/_responsive.scss +++ b/web/sass-files/sass/partials/_responsive.scss @@ -303,12 +303,17 @@ } } @media screen and (max-width: 768px) { + .textarea-wrapper { + .textbox-preview-link { + display: none; + } + } .form-control { &.min-height { min-height: 100px; } } - .gif-div { + .img-div { max-width: 100%; } .tip-div { @@ -535,17 +540,22 @@ .post-create__container { .post-right__container & { padding: 0 1em; + .msg-typing { + display: none; + } } form { - padding: 0; + padding: 0.5em 0; } .post-create-footer { + padding: 0 1em; .msg-typing { - margin-left: 45px; - width: 55%; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; + display: none; + } + .has-error { + .control-label { + height: auto; + } } } .post-create-body { @@ -574,8 +584,8 @@ } } .preview-container { - padding: 0px 10px; - margin-top: 20px; + padding: 0px; + margin-top: 5px; .preview-div { margin-top: 0; } @@ -648,7 +658,7 @@ } .search-bar__container { padding: 0; - height: 45px; + @include flex(0 0 44px); background: $primary-color; color: #fff; &.focused { @@ -777,6 +787,12 @@ .sidebar--right__close { display: none; } + .sidebar-right__body { + height: calc(100% - 44px); + } + } + .search-items-container { + height: calc(100% - 44px); } .inner__wrap { &.move--right { diff --git a/web/sass-files/sass/partials/_search.scss b/web/sass-files/sass/partials/_search.scss index 73b69c866..bedf35376 100644 --- a/web/sass-files/sass/partials/_search.scss +++ b/web/sass-files/sass/partials/_search.scss @@ -1,8 +1,9 @@ #channel-header .search-bar__container { - padding: 8px 8px 8px 0; + padding: 0 8px 0 0; } .search-bar__container { - padding: 12px 8px 12px 0; + padding: 12px 8px 0 0; + @include flex(0 0 56px); } .search__clear { display: none; @@ -71,8 +72,10 @@ .search-items-container { position: relative; - overflow: scroll; + overflow: auto; -webkit-overflow-scrolling: touch; + @include flex(1 1 auto); + height: calc(100% - 56px); } .search-results-header { @@ -92,9 +95,11 @@ padding: 10px 1em; margin: 0; cursor: pointer; + &:first-child { border: none; } + .search-channel__name { font-weight: 600; margin: 0 0 10px 0; diff --git a/web/sass-files/sass/partials/_sidebar--left.scss b/web/sass-files/sass/partials/_sidebar--left.scss index ab13d1b42..eb7c1b83f 100644 --- a/web/sass-files/sass/partials/_sidebar--left.scss +++ b/web/sass-files/sass/partials/_sidebar--left.scss @@ -47,7 +47,7 @@ .nav-pills__container { height: 100%; position: relative; - overflow: scroll; + overflow: auto; -webkit-overflow-scrolling: touch; } diff --git a/web/sass-files/sass/partials/_sidebar--menu.scss b/web/sass-files/sass/partials/_sidebar--menu.scss index 6f4a0cc38..e34cd72c1 100644 --- a/web/sass-files/sass/partials/_sidebar--menu.scss +++ b/web/sass-files/sass/partials/_sidebar--menu.scss @@ -57,8 +57,11 @@ color: inherit; line-height: 40px; padding: 0 15px; - .glyphicon { + .fa ,.glyphicon { width: 25px; + text-align: center; + left: -5px; + position: relative; } } } diff --git a/web/sass-files/sass/partials/_sidebar--right.scss b/web/sass-files/sass/partials/_sidebar--right.scss index a4267294c..2527eef28 100644 --- a/web/sass-files/sass/partials/_sidebar--right.scss +++ b/web/sass-files/sass/partials/_sidebar--right.scss @@ -3,24 +3,38 @@ width: 400px; height: 100%; right: 0px; - padding: 0 0 2em 0; + padding: 0; background: #fff; @include single-transition(transform, 0.5s, ease); right: -320px; + &.move--left { right: 0; } + + .sidebar--right__content { + height: 100%; + @include display-flex; + @include flex-direction(column); + } + .sidebar--right__back { - color: #666; - width: 20px; + color: inherit; + @include opacity(0.8); + width: 30px; text-align: center; - margin: 0 0 0 -6px; - font-size: 12px; + margin: 0 0 0 -14px; + font-size: 13px; display: inline-block; } .sidebar-right__body { + @include flex(1 1 auto); border-left: $border-gray; border-top: $border-gray; + @include display-flex; + @include flex-direction(column); + height: calc(100% - 56px); + @include border-radius(2px 0 0 0); } .post { .post-header { @@ -75,7 +89,7 @@ height: 44px; padding: 0 1em; line-height: 44px; - background: #F5F5F5; + @include flex(0 0 44px); border-bottom: $border-gray; } .sidebar--right__subheader { diff --git a/web/sass-files/sass/partials/_videos.scss b/web/sass-files/sass/partials/_videos.scss index bb36b6223..3f15f8f1e 100644 --- a/web/sass-files/sass/partials/_videos.scss +++ b/web/sass-files/sass/partials/_videos.scss @@ -51,7 +51,7 @@ border-left:60px solid rgba(255,255,255,0.4); } -.gif-div { +.img-div { -moz-force-broken-image-icon: 1; position:relative; max-width: 450px; diff --git a/web/web.go b/web/web.go index ffc791cb7..02ceb69ba 100644 --- a/web/web.go +++ b/web/web.go @@ -989,7 +989,7 @@ func incomingWebhook(c *api.Context, w http.ResponseWriter, r *http.Request) { } text := parsedRequest.Text - if len(text) == 0 { + if len(text) == 0 && parsedRequest.Attachments == nil { c.Err = model.NewAppError("incomingWebhook", "No text specified", "") return } |