diff options
Diffstat (limited to 'web/react')
-rw-r--r-- | web/react/components/channel_header.jsx | 15 | ||||
-rw-r--r-- | web/react/components/mention_list.jsx | 6 | ||||
-rw-r--r-- | web/react/components/new_channel.jsx | 7 | ||||
-rw-r--r-- | web/react/components/post_list.jsx | 15 | ||||
-rw-r--r-- | web/react/components/setting_picture.jsx | 30 | ||||
-rw-r--r-- | web/react/components/sidebar_header.jsx | 117 | ||||
-rw-r--r-- | web/react/components/signup_user_complete.jsx | 200 | ||||
-rw-r--r-- | web/react/components/user_profile.jsx | 3 | ||||
-rw-r--r-- | web/react/components/user_settings.jsx | 240 | ||||
-rw-r--r-- | web/react/utils/constants.jsx | 2 |
10 files changed, 376 insertions, 259 deletions
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index b6182cfa5..90a776791 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -67,7 +67,7 @@ var PopoverListMembers = React.createClass({ return ( <div id='member_popover' data-toggle='popover' data-content={popoverHtml} data-original-title='Members' > - <div id='member_tooltip' data-toggle='tooltip' title='View Channel Members'> + <div id='member_tooltip' data-placement='left' data-toggle='tooltip' title='View Channel Members'> {count} <span className='glyphicon glyphicon-user' aria-hidden='true'></span> </div> </div> @@ -175,6 +175,11 @@ module.exports = React.createClass({ } } + var channelTerm = 'Channel'; + if (channel.type === 'P') { + channelTerm = 'Group'; + } + return ( <table className='channel-header alt'> <tr> @@ -196,18 +201,18 @@ module.exports = React.createClass({ <li role='presentation'><a role='menuitem' data-toggle='modal' data-target='#channel_members' href='#'>Manage Members</a></li> : null } - <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li> + <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set {channelTerm} Description...</a></li> <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#channel_notifications' data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li> {isAdmin && !ChannelStore.isDefault(channel) ? - <li 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}>Rename Channel...</a></li> + <li 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}>Rename {channelTerm}...</a></li> : null } {isAdmin && !ChannelStore.isDefault(channel) ? - <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> + <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#delete_channel' data-title={channel.display_name} data-channelid={channel.id}>Delete {channelTerm}...</a></li> : null } {!ChannelStore.isDefault(channel) ? - <li role='presentation'><a role='menuitem' href='#' onClick={this.handleLeave}>Leave Channel</a></li> + <li role='presentation'><a role='menuitem' href='#' onClick={this.handleLeave}>Leave {channelTerm}</a></li> : null } </ul> diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index a0f68df98..5f1bb6d0e 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -20,6 +20,12 @@ module.exports = React.createClass({ PostStore.addMentionDataChangeListener(this.onListenerChange); var self = this; + $('.post-right__scroll').scroll(function(){ + if($('.mentions--top').length){ + $('#reply_mention_tab .mentions--top').css({ bottom: $(window).height() - $('.post-right__scroll #reply_textbox').offset().top }); + } + }); + $('body').on('keydown.mentionlist', '#' + this.props.id, function(e) { if (!self.isEmpty() && self.state.mentionText !== '-1' && (e.which === 13 || e.which === 9)) { diff --git a/web/react/components/new_channel.jsx b/web/react/components/new_channel.jsx index 93884f6eb..ffcbfd32d 100644 --- a/web/react/components/new_channel.jsx +++ b/web/react/components/new_channel.jsx @@ -107,6 +107,11 @@ module.exports = React.createClass({ serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>; } + var channelTerm = 'Channel'; + if (this.state.channelType === 'P') { + channelTerm = 'Group'; + } + return ( <div className='modal fade' id='new_channel' ref='modal' tabIndex='-1' role='dialog' aria-hidden='true'> <div className='modal-dialog'> @@ -116,7 +121,7 @@ module.exports = React.createClass({ <span aria-hidden='true'>×</span> <span className='sr-only'>Cancel</span> </button> - <h4 className='modal-title'>New Channel</h4> + <h4 className='modal-title'>New {channelTerm}</h4> </div> <form role='form'> <div className='modal-body'> diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 3f59d5843..bb1b1704c 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -37,15 +37,23 @@ module.exports = React.createClass({ componentDidMount: function() { var user = UserStore.getCurrentUser(); if (user.props && user.props.theme) { - utils.changeCss('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;'); utils.changeCss('div.theme', 'background-color:'+user.props.theme+';'); utils.changeCss('.btn.btn-primary', 'background: ' + user.props.theme+';'); - utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, -10) +';'); utils.changeCss('.modal .modal-header', 'background: ' + user.props.theme+';'); utils.changeCss('.mention', 'background: ' + user.props.theme+';'); utils.changeCss('.mention-link', 'color: ' + user.props.theme+';'); utils.changeCss('@media(max-width: 768px){.search-bar__container', 'background: ' + user.props.theme+';}'); } + if (user.props.theme != '#000000' && user.props.theme != '#585858') { + utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, -10) +';'); + utils.changeCss('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;'); + } else if (user.props.theme == '#000000') { + utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, +50) +';'); + $('.team__header').addClass('theme--black'); + } else if (user.props.theme == '#585858') { + utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, +10) +';'); + $('.team__header').addClass('theme--gray'); + } PostStore.addChangeListener(this._onChange); ChannelStore.addChangeListener(this._onChange); @@ -311,7 +319,6 @@ module.exports = React.createClass({ } else if (channel.type === 'D') { var teammate = utils.getDirectTeammate(channel.id) - if (teammate) { var teammate_name = teammate.nickname.length > 0 ? teammate.nickname : teammate.username; more_messages = ( @@ -399,7 +406,7 @@ module.exports = React.createClass({ var postCtls = []; if (posts) { - var previousPostDay = posts[order[order.length-1]] ? utils.getDateForUnixTicks(posts[order[order.length-1]].create_at): new Date(); + var previousPostDay = new Date(0); var currentPostDay; for (var i = order.length-1; i >= 0; i--) { diff --git a/web/react/components/setting_picture.jsx b/web/react/components/setting_picture.jsx index fa4c8bb62..e97b67706 100644 --- a/web/react/components/setting_picture.jsx +++ b/web/react/components/setting_picture.jsx @@ -20,8 +20,14 @@ module.exports = React.createClass({ } }, render: function() { - var client_error = this.props.client_error ? <div className='form-group has-error'><label className='control-label'>{ this.props.client_error }</label></div> : null; - var server_error = this.props.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.props.server_error }</label></div> : null; + var clientError = null; + if (this.props.client_error) { + clientError = <div className='form-group has-error'><label className='control-label'>{this.props.client_error}</label></div>; + } + var serverError = null; + if (this.props.server_error) { + serverError = <div className='form-group has-error'><label className='control-label'>{this.props.server_error}</label></div>; + } var img = null; if (this.props.picture) { @@ -30,8 +36,20 @@ module.exports = React.createClass({ img = (<img ref='image' className='profile-img' src={this.props.src}/>); } - var self = this; + var confirmButton; + if (this.props.loadingPicture) { + confirmButton = <img className='spinner' src='/static/images/load.gif'/>; + } else { + var confirmButtonClass = 'btn btn-sm'; + if (this.props.submitActive) { + confirmButtonClass += ' btn-primary'; + } else { + confirmButtonClass += ' btn-inactive disabled'; + } + confirmButton = <a className={confirmButtonClass} onClick={this.props.submit}>Save</a>; + } + var self = this; return ( <ul className='section-max'> <li className='col-xs-12 section-title'>{this.props.title}</li> @@ -41,10 +59,10 @@ module.exports = React.createClass({ {img} </li> <li className='setting-list-item'> - {server_error} - {client_error} + {serverError} + {clientError} <span className='btn btn-sm btn-primary btn-file sel-btn'>Select<input ref='input' accept='.jpg,.png,.bmp' type='file' onChange={this.props.pictureChange}/></span> - <a className={this.props.submitActive ? 'btn btn-sm btn-primary' : 'btn btn-sm btn-inactive disabled'} onClick={this.props.submit}>Save</a> + {confirmButton} <a className='btn btn-sm theme' href='#' onClick={self.props.updateSection}>Cancel</a> </li> </ul> diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx index e01ddcd05..ba8f9bacd 100644 --- a/web/react/components/sidebar_header.jsx +++ b/web/react/components/sidebar_header.jsx @@ -1,15 +1,15 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. - var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); var UserStore = require('../stores/user_store.jsx'); +var TeamStore = require('../stores/team_store.jsx'); var Constants = require('../utils/constants.jsx'); function getStateFromStores() { - return { teams: UserStore.getTeams() }; + return {teams: UserStore.getTeams(), currentTeam: TeamStore.getCurrent()}; } var NavbarDropdown = React.createClass({ @@ -19,20 +19,24 @@ var NavbarDropdown = React.createClass({ }, blockToggle: false, componentDidMount: function() { - UserStore.addTeamsChangeListener(this._onChange); + UserStore.addTeamsChangeListener(this.onListenerChange); + TeamStore.addChangeListener(this.onListenerChange); var self = this; - $(this.refs.dropdown.getDOMNode()).on('hide.bs.dropdown', function(e) { + $(this.refs.dropdown.getDOMNode()).on('hide.bs.dropdown', function() { self.blockToggle = true; - setTimeout(function(){self.blockToggle = false;}, 100); + setTimeout(function() { + self.blockToggle = false; + }, 100); }); }, componentWillUnmount: function() { - UserStore.removeTeamsChangeListener(this._onChange); + UserStore.removeTeamsChangeListener(this.onListenerChange); + TeamStore.removeChangeListener(this.onListenerChange); $(this.refs.dropdown.getDOMNode()).off('hide.bs.dropdown'); }, - _onChange: function() { + onListenerChange: function() { if (this.isMounted()) { var newState = getStateFromStores(); if (!utils.areStatesEqual(newState, this.state)) { @@ -44,62 +48,65 @@ var NavbarDropdown = React.createClass({ return getStateFromStores(); }, render: function() { - var team_link = ""; - var invite_link = ""; - var manage_link = ""; - var rename_link = ""; + var teamLink = ''; + var inviteLink = ''; + var manageLink = ''; + var renameLink = ''; var currentUser = UserStore.getCurrentUser(); var isAdmin = false; + var teamSettings = null; if (currentUser != null) { - isAdmin = currentUser.roles.indexOf("admin") > -1; + isAdmin = currentUser.roles.indexOf('admin') > -1; - invite_link = ( <li> <a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a> </li>); + inviteLink = (<li> <a href='#' data-toggle='modal' data-target='#invite_member'>Invite New Member</a> </li>); - if (this.props.teamType == "O") { - team_link = ( + if (this.props.teamType === 'O') { + teamLink = ( <li> - <a href="#" data-toggle="modal" data-target="#get_link" data-title="Team Invite" data-value={location.origin+"/signup_user_complete/?id="+currentUser.team_id}>Get Team Invite Link</a> + <a href='#' data-toggle='modal' data-target='#get_link' data-title='Team Invite' data-value={location.origin + '/signup_user_complete/?id=' + currentUser.team_id}>Get Team Invite Link</a> </li> ); } } if (isAdmin) { - manage_link = ( <li> <a href="#" data-toggle="modal" data-target="#team_members">Manage Team</a> </li>); - rename_link = ( <li> <a href="#" data-toggle="modal" data-target="#rename_team_link">Rename</a> </li>); + manageLink = (<li> <a href='#' data-toggle='modal' data-target='#team_members'>Manage Team</a> </li>); + renameLink = (<li> <a href='#' data-toggle='modal' data-target='#rename_team_link'>Rename</a> </li>); + teamSettings = (<li> <a href='#' data-toggle='modal' data-target='#team_settings'>Team Settings</a> </li>); } var teams = []; - teams.push(<li className="divider" key="div"></li>); - if (this.state.teams.length > 1) { - for (var i = 0; i < this.state.teams.length; i++) { - var teamName = this.state.teams[i]; - - teams.push(<li key={ teamName }><a href={utils.getWindowLocationOrigin() + "/" + teamName }>Switch to { teamName }</a></li>); - } + teams.push(<li className='divider' key='div'></li>); + if (this.state.teams.length > 1 && this.state.currentTeam) { + var curTeamName = this.state.currentTeam.name; + this.state.teams.forEach(function(teamName) { + if (teamName !== curTeamName) { + teams.push(<li key={teamName}><a href={utils.getWindowLocationOrigin() + '/' + teamName}>Switch to {teamName}</a></li>); + } + }); } - teams.push(<li><a href={utils.getWindowLocationOrigin() + "/signup_team" }>Create a New Team</a></li>); + teams.push(<li><a href={utils.getWindowLocationOrigin() + '/signup_team'}>Create a New Team</a></li>); return ( - <ul className="nav navbar-nav navbar-right"> - <li ref="dropdown" className="dropdown"> - <a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> - <span className="dropdown__icon" dangerouslySetInnerHTML={{__html: Constants.MENU_ICON }} /> + <ul className='nav navbar-nav navbar-right'> + <li ref='dropdown' className='dropdown'> + <a href='#' className='dropdown-toggle' data-toggle='dropdown' role='button' aria-expanded='false'> + <span className='dropdown__icon' dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} /> </a> - <ul className="dropdown-menu" role="menu"> - <li><a href="#" data-toggle="modal" data-target="#user_settings1">Account Settings</a></li> - { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : null } - { invite_link } - { team_link } - { manage_link } - { rename_link } - <li><a href="#" onClick={this.handleLogoutClick}>Logout</a></li> - { teams } - <li className="divider"></li> - <li><a target="_blank" href={config.HelpLink}>Help</a></li> - <li><a target="_blank" href={config.ReportProblemLink}>Report a Problem</a></li> + <ul className='dropdown-menu' role='menu'> + <li><a href='#' data-toggle='modal' data-target='#user_settings1'>Account Settings</a></li> + {teamSettings} + {inviteLink} + {teamLink} + {manageLink} + {renameLink} + <li><a href='#' onClick={this.handleLogoutClick}>Logout</a></li> + {teams} + <li className='divider'></li> + <li><a target='_blank' href={config.HelpLink}>Help</a></li> + <li><a target='_blank' href={config.ReportProblemLink}>Report a Problem</a></li> </ul> </li> </ul> @@ -109,14 +116,13 @@ var NavbarDropdown = React.createClass({ module.exports = React.createClass({ displayName: 'SidebarHeader', - getDefaultProps: function() { return { teamDisplayName: config.SiteName }; }, - toggleDropdown: function(e) { + toggleDropdown: function() { if (this.refs.dropdown.blockToggle) { this.refs.dropdown.blockToggle = false; return; @@ -126,25 +132,26 @@ module.exports = React.createClass({ render: function() { var me = UserStore.getCurrentUser(); + var profilePicture = null; if (!me) { return null; } + if (me.last_picture_update) { + profilePicture = (<img className='user__picture' src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at} />); + } + return ( - <div className="team__header theme"> - <a href="#" onClick={this.toggleDropdown}> - { me.last_picture_update ? - <img className="user__picture" src={"/api/v1/users/" + me.id + "/image?time=" + me.update_at} /> - : - null - } - <div className="header__info"> - <div className="user__name">{ '@' + me.username}</div> - <div className="team__name">{ this.props.teamDisplayName }</div> + <div className='team__header theme'> + <a href='#' onClick={this.toggleDropdown}> + {profilePicture} + <div className='header__info'> + <div className='user__name'>{'@' + me.username}</div> + <div className='team__name'>{this.props.teamDisplayName }</div> </div> </a> - <NavbarDropdown ref="dropdown" teamType={this.props.teamType} /> + <NavbarDropdown ref='dropdown' teamType={this.props.teamType} /> </div> ); } diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx index 03808e821..b21553d8a 100644 --- a/web/react/components/signup_user_complete.jsx +++ b/web/react/components/signup_user_complete.jsx @@ -1,7 +1,6 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. - var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); var UserStore = require('../stores/user_store.jsx'); @@ -9,36 +8,41 @@ var BrowserStore = require('../stores/browser_store.jsx'); module.exports = React.createClass({ handleSubmit: function(e) { - e.preventDefault(); + e.preventDefault(); this.state.user.username = this.refs.name.getDOMNode().value.trim(); if (!this.state.user.username) { - this.setState({name_error: "This field is required", email_error: "", password_error: "", server_error: ""}); + this.setState({nameError: 'This field is required', emailError: '', passwordError: '', serverError: ''}); return; } - var username_error = utils.isValidUsername(this.state.user.username); - if (username_error === "Cannot use a reserved word as a username.") { - this.setState({name_error: "This username is reserved, please choose a new one.", email_error: "", password_error: "", server_error: ""}); + var usernameError = utils.isValidUsername(this.state.user.username); + if (usernameError === 'Cannot use a reserved word as a username.') { + this.setState({nameError: 'This username is reserved, please choose a new one.', emailError: '', passwordError: '', serverError: ''}); return; - } else if (username_error) { - this.setState({name_error: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'.", email_error: "", password_error: "", server_error: ""}); + } else if (usernameError) { + this.setState({ + nameError: 'Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\' and \'_\'.', + emailError: '', + passwordError: '', + serverError: '' + }); return; } this.state.user.email = this.refs.email.getDOMNode().value.trim(); if (!this.state.user.email) { - this.setState({name_error: "", email_error: "This field is required", password_error: ""}); + this.setState({nameError: '', emailError: 'This field is required', passwordError: ''}); return; } this.state.user.password = this.refs.password.getDOMNode().value.trim(); if (!this.state.user.password || this.state.user.password .length < 5) { - this.setState({name_error: "", email_error: "", password_error: "Please enter at least 5 characters", server_error: ""}); + this.setState({nameError: '', emailError: '', passwordError: 'Please enter at least 5 characters', serverError: ''}); return; } - this.setState({name_error: "", email_error: "", password_error: "", server_error: ""}); + this.setState({nameError: '', emailError: '', passwordError: '', serverError: ''}); this.state.user.allow_marketing = true; @@ -50,108 +54,154 @@ module.exports = React.createClass({ function(data) { UserStore.setLastEmail(this.state.user.email); UserStore.setCurrentUser(data); - if (this.props.hash > 0) - { - BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: "finished"})); + if (this.props.hash > 0) { + BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'})); } window.location.href = '/'; }.bind(this), function(err) { - if (err.message == "Login failed because email address has not been verified") { - window.location.href = "/verify_email?email="+ encodeURIComponent(this.state.user.email) + "&teamname=" + encodeURIComponent(this.props.teamName); + if (err.message === 'Login failed because email address has not been verified') { + window.location.href = '/verify_email?email=' + encodeURIComponent(this.state.user.email) + '&teamname=' + encodeURIComponent(this.props.teamName); } else { - this.state.server_error = err.message; - this.setState(this.state); + this.setState({serverError: err.message}); } }.bind(this) ); }.bind(this), function(err) { - this.state.server_error = err.message; - this.setState(this.state); + this.setState({serverError: err.message}); }.bind(this) ); }, getInitialState: function() { - var props = BrowserStore.getGlobalItem(this.props.hash); - - if (!props) { - props = {}; - props.wizard = "welcome"; - props.user = {}; - props.user.team_id = this.props.teamId; - props.user.email = this.props.email; - props.hash = this.props.hash; - props.data = this.props.data; - props.original_email = this.props.email; + var state = BrowserStore.getGlobalItem(this.props.hash); + + if (!state) { + state = {}; + state.wizard = 'welcome'; + state.user = {}; + state.user.team_id = this.props.teamId; + state.user.email = this.props.email; + state.hash = this.props.hash; + state.data = this.props.data; + state.original_email = this.props.email; } - return props; + return state; }, render: function() { - client.track('signup', 'signup_user_01_welcome'); - if (this.state.wizard == "finished") { - return (<div>You've already completed the signup process for this invitation or this invitation has expired.</div>); + if (this.state.wizard === 'finished') { + return <div>You've already completed the signup process for this invitation or this invitation has expired.</div>; + } + + // set up error labels + var emailError = null; + var emailDivStyle = 'form-group'; + if (this.state.emailError) { + emailError = <label className='control-label'>{this.state.emailError}</label>; + emailDivStyle += ' has-error'; + } + + var nameError = null; + var nameDivStyle = 'form-group'; + if (this.state.nameError) { + nameError = <label className='control-label'>{this.state.nameError}</label>; + nameDivStyle += ' has-error'; + } + + var passwordError = null; + var passwordDivStyle = 'form-group'; + if (this.state.passwordError) { + passwordError = <label className='control-label'>{this.state.passwordError}</label>; + passwordDivStyle += ' has-error'; + } + + var serverError = null; + if (this.state.serverError) { + serverError = ( + <div className={'form-group has-error'}> + <label className='control-label'>{this.state.serverError}</label> + </div> + ); } - var email_error = this.state.email_error ? <label className='control-label'>{ this.state.email_error }</label> : null; - var name_error = this.state.name_error ? <label className='control-label'>{ this.state.name_error }</label> : null; - var password_error = this.state.password_error ? <label className='control-label'>{ this.state.password_error }</label> : null; - var server_error = this.state.server_error ? <div className={ "form-group has-error" }><label className='control-label'>{ this.state.server_error }</label></div> : null; + // set up the email entry and hide it if an email was provided + var yourEmailIs = ''; + if (this.state.user.email) { + yourEmailIs = <span>Your email address is {this.state.user.email}. You'll use this address to sign in to {config.SiteName}.</span>; + } - var yourEmailIs = this.state.user.email == "" ? "" : <span>Your email address is { this.state.user.email }. </span> + var emailContainerStyle = "margin--extra"; + if (this.state.original_email) { + emailContainerStyle = "hidden"; + } var email = ( - <div className={ this.state.original_email == "" ? "margin--extra" : "hidden"} > + <div className={emailContainerStyle}> <h5><strong>What's your email address?</strong></h5> - <div className={ email_error ? "form-group has-error" : "form-group" }> - <input type="email" ref="email" className="form-control" defaultValue={ this.state.user.email } placeholder="" maxLength="128" autoFocus={true} /> - { email_error } - </div> + <div className={emailDivStyle}> + <input type='email' ref='email' className='form-control' defaultValue={this.state.user.email} placeholder='' maxLength='128' autoFocus={true} /> + {emailError} </div> + </div> ); - var auth_services = JSON.parse(this.props.authServices); + // add options to log in using another service + var authServices = JSON.parse(this.props.authServices); + + var signupMessage = null; + if (authServices.indexOf('gitlab') >= 0) { + signupMessage = ( + <div> + <a className='btn btn-custom-login gitlab' href={'/' + this.props.teamName + '/signup/gitlab' + window.location.search}> + <span className='icon' /> + <span>with GitLab</span> + </a> + <div className='or__container'> + <span>or</span> + </div> + </div> + ); + } - var signup_message; - if (auth_services.indexOf("gitlab") >= 0) { - signup_message = <div><a className="btn btn-custom-login gitlab" href={"/"+this.props.teamName+"/signup/gitlab"+window.location.search}><span className="icon" />{"with GitLab"}</a> - <div className="or__container"><span>or</span></div></div>; + var termsDisclaimer = null; + if (config.ShowTermsDuringSignup) { + termsDisclaimer = <p>By creating an account and using Mattermost you are agreeing to our <a href={config.TermsLink}>Terms of Service</a>. If you do not agree, you cannot use this service.</p>; } return ( <div> <form> - <img className="signup-team-logo" src="/static/images/logo.png" /> - <h5 className="margin--less">Welcome to:</h5> - <h2 className="signup-team__name">{ this.props.teamDisplayName }</h2> - <h2 className="signup-team__subdomain">on { config.SiteName }</h2> - <h4 className="color--light">Let's create your account</h4> - { signup_message } - <div className="inner__content"> - { email } - <p className={ this.state.original_email == "" ? "hidden" : ""}>{ yourEmailIs } You’ll use this address to sign in to {config.SiteName}.</p> - <div className="margin--extra"> - <h5><strong>Choose your username</strong></h5> - <div className={ name_error ? "form-group has-error" : "form-group" }> - <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" /> - { name_error } - <p className="form__hint">Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'"</p> + <img className='signup-team-logo' src='/static/images/logo.png' /> + <h5 className='margin--less'>Welcome to:</h5> + <h2 className='signup-team__name'>{this.props.teamDisplayName}</h2> + <h2 className='signup-team__subdomain'>on {config.SiteName}</h2> + <h4 className='color--light'>Let's create your account</h4> + {signupMessage} + <div className='inner__content'> + {email} + {yourEmailIs} + <div className='margin--extra'> + <h5><strong>Choose your username</strong></h5> + <div className={nameDivStyle}> + <input type='text' ref='name' className='form-control' placeholder='' maxLength='128' /> + {nameError} + <p className='form__hint'>Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'</p> + </div> + </div> + <div className='margin--extra'> + <h5><strong>Choose your password</strong></h5> + <div className={passwordDivStyle}> + <input type='password' ref='password' className='form-control' placeholder='' maxLength='128' /> + {passwordError} </div> - </div> - <div className="margin--extra"> - <h5><strong>Choose your password</strong></h5> - <div className={ password_error ? "form-group has-error" : "form-group" }> - <input type="password" ref="password" className="form-control" placeholder="" maxLength="128" /> - { password_error } </div> </div> - </div> - <p className="margin--extra"><button type='submit' onClick={this.handleSubmit} className="btn-primary btn">Create Account</button></p> - { server_error } - <p>By creating an account and using Mattermost you are agreeing to our <a href={ config.TermsLink }>Terms of Service</a>. If you do not agree, you cannot use this service.</p> + <p className='margin--extra'><button type='submit' onClick={this.handleSubmit} className='btn-primary btn'>Create Account</button></p> + {serverError} + {termsDisclaimer} </form> </div> ); diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx index 65f025919..5c4d26a23 100644 --- a/web/react/components/user_profile.jsx +++ b/web/react/components/user_profile.jsx @@ -28,6 +28,7 @@ module.exports = React.createClass({ componentDidMount: function() { UserStore.addChangeListener(this._onChange); $("#profile_" + this.uniqueId).popover({placement : 'right', container: 'body', trigger: 'hover', html: true, delay: { "show": 200, "hide": 100 }}); + $('body').tooltip( {selector: '[data-toggle=tooltip]', trigger: 'hover click'} ); }, componentWillUnmount: function() { UserStore.removeChangeListener(this._onChange); @@ -57,7 +58,7 @@ module.exports = React.createClass({ if (!config.ShowEmail) { data_content += "<div class='text-nowrap'>Email not shared</div>"; } else { - data_content += "<div><a href='mailto:" + this.state.profile.email + "' class='text-nowrap text-lowercase'>" + this.state.profile.email + "</a></div>"; + data_content += "<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>"; } return ( diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index c574d2365..1a0c313d3 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -5,8 +5,6 @@ var UserStore = require('../stores/user_store.jsx'); var SettingItemMin = require('./setting_item_min.jsx'); var SettingItemMax = require('./setting_item_max.jsx'); var SettingPicture = require('./setting_picture.jsx'); -var AccessHistoryModal = require('./access_history_modal.jsx'); -var ActivityLogModal = require('./activity_log_modal.jsx'); var client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var utils = require('../utils/utils.jsx'); @@ -463,22 +461,22 @@ var SecurityTab = React.createClass({ e.preventDefault(); var user = this.props.user; - var currentPassword = this.state.current_password; - var newPassword = this.state.new_password; - var confirmPassword = this.state.confirm_password; + var currentPassword = this.state.currentPassword; + var newPassword = this.state.newPassword; + var confirmPassword = this.state.confirmPassword; if (currentPassword === '') { - this.setState({password_error: 'Please enter your current password', server_error: ''}); + this.setState({passwordError: 'Please enter your current password', serverError: ''}); return; } if (newPassword.length < 5) { - this.setState({password_error: 'New passwords must be at least 5 characters', server_error: ''}); + this.setState({passwordError: 'New passwords must be at least 5 characters', serverError: ''}); return; } if (newPassword !== confirmPassword) { - this.setState({password_error: 'The new passwords you entered do not match', server_error: ''}); + this.setState({passwordError: 'The new passwords you entered do not match', serverError: ''}); return; } @@ -488,43 +486,43 @@ var SecurityTab = React.createClass({ data.new_password = newPassword; client.updatePassword(data, - function(data) { - this.props.updateSection(""); + function() { + this.props.updateSection(''); AsyncClient.getMe(); - this.setState({current_password: '', new_password: '', confirm_password: ''}); + this.setState({currentPassword: '', newPassword: '', confirmPassword: ''}); }.bind(this), function(err) { var state = this.getInitialState(); if (err.message) { - state.server_error = err.message; + state.serverError = err.message; } else { - state.server_error = err; + state.serverError = err; } - state.password_error = ''; + state.passwordError = ''; this.setState(state); }.bind(this) ); }, updateCurrentPassword: function(e) { - this.setState({ current_password: e.target.value }); + this.setState({currentPassword: e.target.value}); }, updateNewPassword: function(e) { - this.setState({ new_password: e.target.value }); + this.setState({newPassword: e.target.value}); }, updateConfirmPassword: function(e) { - this.setState({ confirm_password: e.target.value }); + this.setState({confirmPassword: e.target.value}); }, handleHistoryOpen: function() { - $("#user_settings1").modal('hide'); + $('#user_settings1').modal('hide'); }, handleDevicesOpen: function() { - $("#user_settings1").modal('hide'); + $('#user_settings1').modal('hide'); }, handleClose: function() { - $(this.getDOMNode()).find(".form-control").each(function() { - this.value = ""; + $(this.getDOMNode()).find('.form-control').each(function() { + this.value = ''; }); - this.setState({current_password: '', new_password: '', confirm_password: '', server_error: null, password_error: null}); + this.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null}); this.props.updateTab('general'); }, componentDidMount: function() { @@ -535,40 +533,41 @@ var SecurityTab = React.createClass({ this.props.updateSection(''); }, getInitialState: function() { - return { current_password: '', new_password: '', confirm_password: '' }; + return {currentPassword: '', newPassword: '', confirmPassword: ''}; }, render: function() { - var server_error = this.state.server_error ? this.state.server_error : null; - var password_error = this.state.password_error ? this.state.password_error : null; + var serverError = this.state.serverError ? this.state.serverError : null; + var passwordError = this.state.passwordError ? this.state.passwordError : null; + var updateSectionStatus; var passwordSection; var self = this; if (this.props.activeSection === 'password') { var inputs = []; var submit = null; - if (this.props.user.auth_service === "") { + if (this.props.user.auth_service === '') { inputs.push( - <div className="form-group"> - <label className="col-sm-5 control-label">Current Password</label> - <div className="col-sm-7"> - <input className="form-control" type="password" onChange={this.updateCurrentPassword} value={this.state.current_password}/> + <div className='form-group'> + <label className='col-sm-5 control-label'>Current Password</label> + <div className='col-sm-7'> + <input className='form-control' type='password' onChange={this.updateCurrentPassword} value={this.state.currentPassword}/> </div> </div> ); inputs.push( - <div className="form-group"> - <label className="col-sm-5 control-label">New Password</label> - <div className="col-sm-7"> - <input className="form-control" type="password" onChange={this.updateNewPassword} value={this.state.new_password}/> + <div className='form-group'> + <label className='col-sm-5 control-label'>New Password</label> + <div className='col-sm-7'> + <input className='form-control' type='password' onChange={this.updateNewPassword} value={this.state.newPassword}/> </div> </div> ); inputs.push( - <div className="form-group"> - <label className="col-sm-5 control-label">Retype New Password</label> - <div className="col-sm-7"> - <input className="form-control" type="password" onChange={this.updateConfirmPassword} value={this.state.confirm_password}/> + <div className='form-group'> + <label className='col-sm-5 control-label'>Retype New Password</label> + <div className='col-sm-7'> + <input className='form-control' type='password' onChange={this.updateConfirmPassword} value={this.state.confirmPassword}/> </div> </div> ); @@ -576,58 +575,68 @@ var SecurityTab = React.createClass({ submit = this.submitPassword; } else { inputs.push( - <div className="form-group"> - <label className="col-sm-12">Log in occurs through GitLab. Please see your GitLab account settings page to update your password.</label> + <div className='form-group'> + <label className='col-sm-12'>Log in occurs through GitLab. Please see your GitLab account settings page to update your password.</label> </div> ); } + updateSectionStatus = function(e) { + self.props.updateSection(''); + self.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null}); + e.preventDefault(); + }; + passwordSection = ( <SettingItemMax - title="Password" + title='Password' inputs={inputs} submit={submit} - server_error={server_error} - client_error={password_error} - updateSection={function(e){self.props.updateSection("");e.preventDefault();}} + server_error={serverError} + client_error={passwordError} + updateSection={updateSectionStatus} /> ); } else { var describe; - if (this.props.user.auth_service === "") { + if (this.props.user.auth_service === '') { var d = new Date(this.props.user.last_password_update); - var hour = d.getHours() % 12 ? String(d.getHours() % 12) : "12"; - var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes()); - var timeOfDay = d.getHours() >= 12 ? " pm" : " am"; - describe = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min + timeOfDay; + var hour = d.getHours() % 12 ? String(d.getHours() % 12) : '12'; + var min = d.getMinutes() < 10 ? '0' + d.getMinutes() : String(d.getMinutes()); + var timeOfDay = d.getHours() >= 12 ? ' pm' : ' am'; + describe = 'Last updated ' + Constants.MONTHS[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear() + ' at ' + hour + ':' + min + timeOfDay; } else { - describe = "Log in done through GitLab" + describe = 'Log in done through GitLab'; } + updateSectionStatus = function() { + self.props.updateSection('password'); + }; + passwordSection = ( <SettingItemMin - title="Password" + title='Password' describe={describe} - updateSection={function(){self.props.updateSection("password");}} + updateSection={updateSectionStatus} /> ); } return ( <div> - <div className="modal-header"> - <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> - <h4 className="modal-title" ref="title"><i className="modal-back"></i>Security Settings</h4> + <div className='modal-header'> + <button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>×</span></button> + <h4 className='modal-title' ref='title'><i className='modal-back'></i>Security Settings</h4> </div> - <div className="user-settings"> - <h3 className="tab-header">Security Settings</h3> - <div className="divider-dark first"/> - { passwordSection } - <div className="divider-dark"/> + <div className='user-settings'> + <h3 className='tab-header'>Security Settings</h3> + <div className='divider-dark first'/> + {passwordSection} + <div className='divider-dark'/> <br></br> - <a data-toggle="modal" className="security-links theme" data-target="#access-history" href="#" onClick={this.handleHistoryOpen}><i className="fa fa-clock-o"></i>View Access History</a> - <b> </b> - <a data-toggle="modal" className="security-links theme" data-target="#activity-log" href="#" onClick={this.handleDevicesOpen}><i className="fa fa-globe"></i>View and Logout of Active Sessions</a> + <a data-toggle='modal' className='security-links theme' data-target='#access-history' href='#' onClick={this.handleHistoryOpen}><i className='fa fa-clock-o'></i>View Access History</a> + <b> </b> + <a data-toggle='modal' className='security-links theme' data-target='#activity-log' href='#' onClick={this.handleDevicesOpen}><i className='fa fa-globe'></i>View and Logout of Active Sessions</a> </div> </div> ); @@ -642,17 +651,17 @@ var GeneralTab = React.createClass({ var user = this.props.user; var username = this.state.username.trim(); - var username_error = utils.isValidUsername(username); - if (username_error === 'Cannot use a reserved word as a username.') { - this.setState({client_error: 'This username is reserved, please choose a new one.' }); + var 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; - } else if (username_error) { - this.setState({client_error: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'." }); + } else if (usernameError) { + this.setState({clientError: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."}); return; } if (user.username === username) { - this.setState({client_error: 'You must submit a new username'}); + this.setState({clientError: 'You must submit a new username'}); return; } @@ -667,7 +676,7 @@ var GeneralTab = React.createClass({ var nickname = this.state.nickname.trim(); if (user.nickname === nickname) { - this.setState({client_error: 'You must submit a new nickname'}) + this.setState({clientError: 'You must submit a new nickname'}); return; } @@ -679,11 +688,11 @@ var GeneralTab = React.createClass({ e.preventDefault(); var user = UserStore.getCurrentUser(); - var firstName = this.state.first_name.trim(); - var lastName = this.state.last_name.trim(); + var firstName = this.state.firstName.trim(); + var lastName = this.state.lastName.trim(); if (user.first_name === firstName && user.last_name === lastName) { - this.setState({client_error: 'You must submit a new first or last name'}) + this.setState({clientError: 'You must submit a new first or last name'}); return; } @@ -703,7 +712,7 @@ var GeneralTab = React.createClass({ } if (email === '' || !utils.isEmail(email)) { - this.setState({ email_error: 'Please enter a valid email address' }); + this.setState({emailError: 'Please enter a valid email address'}); return; } @@ -718,11 +727,11 @@ var GeneralTab = React.createClass({ AsyncClient.getMe(); }.bind(this), function(err) { - state = this.getInitialState(); + var state = this.getInitialState(); if (err.message) { - state.server_error = err.message; + state.serverError = err.message; } else { - state.server_error = err; + state.serverError = err; } this.setState(state); }.bind(this) @@ -742,12 +751,13 @@ var GeneralTab = React.createClass({ var picture = this.state.picture; if (picture.type !== 'image/jpeg' && picture.type !== 'image/png') { - this.setState({client_error: '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; } var formData = new FormData(); formData.append('image', picture, picture.name); + this.setState({loadingPicture: true}); client.uploadProfileImage(formData, function() { @@ -756,8 +766,8 @@ var GeneralTab = React.createClass({ window.location.reload(); }.bind(this), function(err) { - state = this.getInitialState(); - state.server_error = err; + var state = this.getInitialState(); + state.serverError = err; this.setState(state); }.bind(this) ); @@ -766,10 +776,10 @@ var GeneralTab = React.createClass({ this.setState({username: e.target.value}); }, updateFirstName: function(e) { - this.setState({first_name: e.target.value}); + this.setState({firstName: e.target.value}); }, updateLastName: function(e) { - this.setState({last_name: e.target.value}); + this.setState({lastName: e.target.value}); }, updateNickname: function(e) { this.setState({nickname: e.target.value}); @@ -779,17 +789,16 @@ var GeneralTab = React.createClass({ }, updatePicture: function(e) { if (e.target.files && e.target.files[0]) { - this.setState({ picture: e.target.files[0] }); + this.setState({picture: e.target.files[0]}); this.submitActive = true; - this.setState({client_error: null}); - + this.setState({clientError: null}); } else { this.setState({picture: null}); } }, updateSection: function(section) { - this.setState({client_error:''}); + this.setState({clientError: ''}); this.submitActive = false; this.props.updateSection(section); }, @@ -798,7 +807,7 @@ var GeneralTab = React.createClass({ this.value = ''; }); - this.setState(assign({}, this.getInitialState(), {client_error: null, server_error: null, email_error: null})); + this.setState(assign({}, this.getInitialState(), {clientError: null, serverError: null, emailError: null})); this.props.updateSection(''); }, componentDidMount: function() { @@ -810,15 +819,24 @@ var GeneralTab = React.createClass({ getInitialState: function() { var user = this.props.user; - return { username: user.username, first_name: user.first_name, last_name: user.last_name, nickname: user.nickname, - email: user.email, picture: null }; + return {username: user.username, firstName: user.first_name, lastName: user.last_name, nickname: user.nickname, + email: user.email, picture: null, loadingPicture: false}; }, render: function() { var user = this.props.user; - var client_error = this.state.client_error ? this.state.client_error : null; - var server_error = this.state.server_error ? this.state.server_error : null; - var email_error = this.state.email_error ? this.state.email_error : null; + var clientError = null; + if (this.state.clientError) { + clientError = this.state.clientError; + } + var serverError = null; + if (this.state.serverError) { + serverError = this.state.serverError; + } + var emailError = null; + if (this.state.emailError) { + emailError = this.state.emailError; + } var nameSection; var self = this; @@ -829,7 +847,7 @@ var GeneralTab = React.createClass({ <div className='form-group'> <label className='col-sm-5 control-label'>First Name</label> <div className='col-sm-7'> - <input className='form-control' type='text' onChange={this.updateFirstName} value={this.state.first_name}/> + <input className='form-control' type='text' onChange={this.updateFirstName} value={this.state.firstName}/> </div> </div> ); @@ -838,7 +856,7 @@ var GeneralTab = React.createClass({ <div className='form-group'> <label className='col-sm-5 control-label'>Last Name</label> <div className='col-sm-7'> - <input className='form-control' type='text' onChange={this.updateLastName} value={this.state.last_name}/> + <input className='form-control' type='text' onChange={this.updateLastName} value={this.state.lastName}/> </div> </div> ); @@ -848,8 +866,8 @@ var GeneralTab = React.createClass({ title='Full Name' inputs={inputs} submit={this.submitName} - server_error={server_error} - client_error={client_error} + server_error={serverError} + client_error={clientError} updateSection={function(e) { self.updateSection(''); e.preventDefault(); @@ -857,20 +875,20 @@ var GeneralTab = React.createClass({ /> ); } else { - var full_name = ''; + var fullName = ''; if (user.first_name && user.last_name) { - full_name = user.first_name + ' ' + user.last_name; + fullName = user.first_name + ' ' + user.last_name; } else if (user.first_name) { - full_name = user.first_name; + fullName = user.first_name; } else if (user.last_name) { - full_name = user.last_name; + fullName = user.last_name; } nameSection = ( <SettingItemMin title='Full Name' - describe={full_name} + describe={fullName} updateSection={function() { self.updateSection('name'); }} @@ -880,7 +898,6 @@ var GeneralTab = React.createClass({ var nicknameSection; if (this.props.activeSection === 'nickname') { - inputs.push( <div className='form-group'> <label className='col-sm-5 control-label'>{utils.isMobile() ? '' : 'Nickname'}</label> @@ -895,8 +912,8 @@ var GeneralTab = React.createClass({ title='Nickname' inputs={inputs} submit={this.submitNickname} - server_error={server_error} - client_error={client_error} + server_error={serverError} + client_error={clientError} updateSection={function(e) { self.updateSection(''); e.preventDefault(); @@ -919,7 +936,7 @@ var GeneralTab = React.createClass({ if (this.props.activeSection === 'username') { inputs.push( <div className='form-group'> - <label className='col-sm-5 control-label'>{utils.isMobile() ? '': 'Username'}</label> + <label className='col-sm-5 control-label'>{utils.isMobile() ? '' : 'Username'}</label> <div className='col-sm-7'> <input className='form-control' type='text' onChange={this.updateUsername} value={this.state.username}/> </div> @@ -931,8 +948,8 @@ var GeneralTab = React.createClass({ title='Username' inputs={inputs} submit={this.submitUsername} - server_error={server_error} - client_error={client_error} + server_error={serverError} + client_error={clientError} updateSection={function(e) { self.updateSection(''); e.preventDefault(); @@ -966,8 +983,8 @@ var GeneralTab = React.createClass({ title='Email' inputs={inputs} submit={this.submitEmail} - server_error={server_error} - client_error={email_error} + server_error={serverError} + client_error={emailError} updateSection={function(e) { self.updateSection(''); e.preventDefault(); @@ -993,8 +1010,8 @@ var GeneralTab = React.createClass({ title='Profile Picture' submit={this.submitPicture} src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update} - server_error={server_error} - client_error={client_error} + server_error={serverError} + client_error={clientError} updateSection={function(e) { self.updateSection(''); e.preventDefault(); @@ -1002,6 +1019,7 @@ var GeneralTab = React.createClass({ picture={this.state.picture} pictureChange={this.updatePicture} submitActive={this.submitActive} + loadingPicture={this.state.loadingPicture} /> ); } else { diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 2b0976afd..bed0ec556 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -82,7 +82,7 @@ module.exports = { "channel", ], MONTHS: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - MAX_DMS: 10, + MAX_DMS: 20, ONLINE_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns#' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' sodipodi:docname='TRASH_1_4.svg' inkscape:version='0.48.4 r9939' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='12px' height='12px' viewBox='0 0 12 12' enable-background='new 0 0 12 12' xml:space='preserve'><sodipodi:namedview inkscape:cy='139.7898' inkscape:cx='26.358185' inkscape:zoom='1.18' showguides='true' showgrid='false' id='namedview6' guidetolerance='10' gridtolerance='10' objecttolerance='10' borderopacity='1' bordercolor='#666666' pagecolor='#ffffff' inkscape:current-layer='Layer_1' inkscape:window-maximized='1' inkscape:window-y='-8' inkscape:window-x='-8' inkscape:window-height='705' inkscape:window-width='1366' inkscape:guide-bbox='true' inkscape:pageshadow='2' inkscape:pageopacity='0'><sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide><sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide></sodipodi:namedview><g><g><path class='online--icon' d='M6,5.487c1.371,0,2.482-1.116,2.482-2.493c0-1.378-1.111-2.495-2.482-2.495S3.518,1.616,3.518,2.994C3.518,4.371,4.629,5.487,6,5.487z M10.452,8.545c-0.101-0.829-0.36-1.968-0.726-2.541C9.475,5.606,8.5,5.5,8.5,5.5S8.43,7.521,6,7.521C3.507,7.521,3.5,5.5,3.5,5.5S2.527,5.606,2.273,6.004C1.908,6.577,1.648,7.716,1.547,8.545C1.521,8.688,1.49,9.082,1.498,9.142c0.161,1.295,2.238,2.322,4.375,2.358C5.916,11.501,5.958,11.501,6,11.501c0.043,0,0.084,0,0.127-0.001c2.076-0.026,4.214-1.063,4.375-2.358C10.509,9.082,10.471,8.696,10.452,8.545z'/></g></g></svg>", OFFLINE_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns#' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' sodipodi:docname='TRASH_1_4.svg' inkscape:version='0.48.4 r9939' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='12px' height='12px' viewBox='0 0 12 12' enable-background='new 0 0 12 12' xml:space='preserve'><sodipodi:namedview inkscape:cy='139.7898' inkscape:cx='26.358185' inkscape:zoom='1.18' showguides='true' showgrid='false' id='namedview6' guidetolerance='10' gridtolerance='10' objecttolerance='10' borderopacity='1' bordercolor='#666666' pagecolor='#ffffff' inkscape:current-layer='Layer_1' inkscape:window-maximized='1' inkscape:window-y='-8' inkscape:window-x='-8' inkscape:window-height='705' inkscape:window-width='1366' inkscape:guide-bbox='true' inkscape:pageshadow='2' inkscape:pageopacity='0'><sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide><sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide></sodipodi:namedview><g><g><path fill='#cccccc' d='M6.002,7.143C5.645,7.363,5.167,7.52,4.502,7.52c-2.493,0-2.5-2.02-2.5-2.02S1.029,5.607,0.775,6.004C0.41,6.577,0.15,7.716,0.049,8.545c-0.025,0.145-0.057,0.537-0.05,0.598c0.162,1.295,2.237,2.321,4.375,2.357c0.043,0.001,0.085,0.001,0.127,0.001c0.043,0,0.084,0,0.127-0.001c1.879-0.023,3.793-0.879,4.263-2h-2.89L6.002,7.143L6.002,7.143z M4.501,5.488c1.372,0,2.483-1.117,2.483-2.494c0-1.378-1.111-2.495-2.483-2.495c-1.371,0-2.481,1.117-2.481,2.495C2.02,4.371,3.13,5.488,4.501,5.488z M7.002,6.5v2h5v-2H7.002z'/></g></g></svg>", MENU_ICON: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>", |