diff options
Diffstat (limited to 'web/react/components')
-rw-r--r-- | web/react/components/create_comment.jsx | 22 | ||||
-rw-r--r-- | web/react/components/create_post.jsx | 22 | ||||
-rw-r--r-- | web/react/components/file_upload.jsx | 101 | ||||
-rw-r--r-- | web/react/components/file_upload_overlay.jsx | 26 | ||||
-rw-r--r-- | web/react/components/login.jsx | 101 | ||||
-rw-r--r-- | web/react/components/post.jsx | 6 | ||||
-rw-r--r-- | web/react/components/post_list.jsx | 7 | ||||
-rw-r--r-- | web/react/components/post_right.jsx | 3 | ||||
-rw-r--r-- | web/react/components/setting_item_min.jsx | 19 | ||||
-rw-r--r-- | web/react/components/sidebar.jsx | 11 | ||||
-rw-r--r-- | web/react/components/signup_user_complete.jsx | 28 | ||||
-rw-r--r-- | web/react/components/signup_user_oauth.jsx | 5 | ||||
-rw-r--r-- | web/react/components/user_settings.jsx | 10 |
13 files changed, 269 insertions, 92 deletions
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 78e06c532..885efab7a 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -122,16 +122,20 @@ module.exports = React.createClass({ this.setState({uploadsInProgress: draft['uploadsInProgress'], previews: draft['previews']}); }, handleUploadError: function(err, clientId) { - var draft = PostStore.getCommentDraft(this.props.rootId); + if (clientId !== -1) { + var draft = PostStore.getCommentDraft(this.props.rootId); - var index = draft['uploadsInProgress'].indexOf(clientId); - if (index !== -1) { - draft['uploadsInProgress'].splice(index, 1); - } + var index = draft['uploadsInProgress'].indexOf(clientId); + if (index !== -1) { + draft['uploadsInProgress'].splice(index, 1); + } - PostStore.storeCommentDraft(this.props.rootId, draft); + PostStore.storeCommentDraft(this.props.rootId, draft); - this.setState({uploadsInProgress: draft['uploadsInProgress'], serverError: err}); + this.setState({uploadsInProgress: draft['uploadsInProgress'], serverError: err}); + } else { + this.setState({serverError: err}); + } }, clearPreviews: function() { this.setState({previews: []}); @@ -222,7 +226,9 @@ module.exports = React.createClass({ getFileCount={this.getFileCount} onUploadStart={this.handleUploadStart} onFileUpload={this.handleFileUploadComplete} - onUploadError={this.handleUploadError} /> + onUploadError={this.handleUploadError} + postType='comment' + channelId={this.props.channelId} /> </div> <MsgTyping channelId={this.props.channelId} parentId={this.props.rootId} /> <div className={postFooterClassName}> diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index 9ca1d5388..377e7bd34 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -145,16 +145,20 @@ module.exports = React.createClass({ this.setState({uploadsInProgress: draft['uploadsInProgress'], previews: draft['previews']}); }, handleUploadError: function(err, clientId) { - var draft = PostStore.getDraft(this.state.channelId); + if (clientId !== -1) { + var draft = PostStore.getDraft(this.state.channelId); - var index = draft['uploadsInProgress'].indexOf(clientId); - if (index !== -1) { - draft['uploadsInProgress'].splice(index, 1); - } + var index = draft['uploadsInProgress'].indexOf(clientId); + if (index !== -1) { + draft['uploadsInProgress'].splice(index, 1); + } - PostStore.storeDraft(this.state.channelId, draft); + PostStore.storeDraft(this.state.channelId, draft); - this.setState({uploadsInProgress: draft['uploadsInProgress'], serverError: err}); + this.setState({uploadsInProgress: draft['uploadsInProgress'], serverError: err}); + } else { + this.setState({serverError: err}); + } }, removePreview: function(id) { var previews = this.state.previews; @@ -262,7 +266,9 @@ module.exports = React.createClass({ getFileCount={this.getFileCount} onUploadStart={this.handleUploadStart} onFileUpload={this.handleFileUploadComplete} - onUploadError={this.handleUploadError} /> + onUploadError={this.handleUploadError} + postType='post' + channelId='' /> </div> <div className={postFooterClassName}> {postError} diff --git a/web/react/components/file_upload.jsx b/web/react/components/file_upload.jsx index c1fab669c..7497ec330 100644 --- a/web/react/components/file_upload.jsx +++ b/web/react/components/file_upload.jsx @@ -12,7 +12,9 @@ module.exports = React.createClass({ onUploadError: React.PropTypes.func, getFileCount: React.PropTypes.func, onFileUpload: React.PropTypes.func, - onUploadStart: React.PropTypes.func + onUploadStart: React.PropTypes.func, + channelId: React.PropTypes.string, + postType: React.PropTypes.string }, getInitialState: function() { return {requests: {}}; @@ -21,7 +23,7 @@ module.exports = React.createClass({ var element = $(this.refs.fileInput.getDOMNode()); var files = element.prop('files'); - var channelId = ChannelStore.getCurrentId(); + var channelId = this.props.channelId || ChannelStore.getCurrentId(); this.props.onUploadError(null); @@ -61,8 +63,8 @@ module.exports = React.createClass({ this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId); var requests = this.state.requests; - for (var i = 0; i < parsedData.client_ids.length; i++) { - delete requests[parsedData.client_ids[i]]; + for (var j = 0; j < parsedData.client_ids.length; j++) { + delete requests[parsedData.client_ids[j]]; } this.setState({requests: requests}); }.bind(this), @@ -87,10 +89,94 @@ module.exports = React.createClass({ } } catch(e) {} }, + handleDrop: function(e) { + this.props.onUploadError(null); + + var files = e.originalEvent.dataTransfer.files; + var channelId = this.props.channelId || ChannelStore.getCurrentId(); + + if (typeof files !== 'string' && files.length) { + var numFiles = files.length; + + var numToUpload = Math.min(Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId), numFiles); + + if (numFiles > numToUpload) { + this.props.onUploadError('Uploads limited to ' + Constants.MAX_UPLOAD_FILES + ' files maximum. Please use additional posts for more files.'); + } + + for (var i = 0; i < files.length && i < numToUpload; i++) { + if (files[i].size > Constants.MAX_FILE_SIZE) { + this.props.onUploadError('Files must be no more than ' + Constants.MAX_FILE_SIZE / 1000000 + ' MB'); + continue; + } + + // generate a unique id that can be used by other components to refer back to this file upload + var clientId = utils.generateId(); + + // Prepare data to be uploaded. + var formData = new FormData(); + formData.append('channel_id', channelId); + formData.append('files', files[i], files[i].name); + formData.append('client_ids', clientId); + + var request = client.uploadFile(formData, + function(data) { + var parsedData = $.parseJSON(data); + this.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId); + + var requests = this.state.requests; + for (var j = 0; j < parsedData.client_ids.length; j++) { + delete requests[parsedData.client_ids[j]]; + } + this.setState({requests: requests}); + }.bind(this), + function(err) { + this.props.onUploadError(err, clientId); + }.bind(this) + ); + + var requests = this.state.requests; + requests[clientId] = request; + this.setState({requests: requests}); + + this.props.onUploadStart([clientId], channelId); + } + } else { + this.props.onUploadError('Invalid file upload', -1); + } + }, componentDidMount: function() { var inputDiv = this.refs.input.getDOMNode(); var self = this; + if (this.props.postType === 'post') { + $('.row.main').dragster({ + enter: function() { + $('.center-file-overlay').removeClass('hidden'); + }, + leave: function() { + $('.center-file-overlay').addClass('hidden'); + }, + drop: function(dragsterEvent, e) { + $('.center-file-overlay').addClass('hidden'); + self.handleDrop(e); + } + }); + } else if (this.props.postType === 'comment') { + $('.post-right__container').dragster({ + enter: function() { + $('.right-file-overlay').removeClass('hidden'); + }, + leave: function() { + $('.right-file-overlay').addClass('hidden'); + }, + drop: function(dragsterEvent, e) { + $('.right-file-overlay').addClass('hidden'); + self.handleDrop(e); + } + }); + } + document.addEventListener('paste', function(e) { var textarea = $(inputDiv.parentNode.parentNode).find('.custom-textarea')[0]; @@ -133,14 +219,13 @@ module.exports = React.createClass({ continue; } - var channelId = ChannelStore.getCurrentId(); + var channelId = this.props.channelId || ChannelStore.getCurrentId(); // generate a unique id that can be used by other components to refer back to this file upload var clientId = utils.generateId(); var formData = new FormData(); formData.append('channel_id', channelId); - var d = new Date(); var hour; if (d.getHours() < 10) { @@ -165,8 +250,8 @@ module.exports = React.createClass({ self.props.onFileUpload(parsedData.filenames, parsedData.client_ids, channelId); var requests = self.state.requests; - for (var i = 0; i < parsedData.client_ids.length; i++) { - delete requests[parsedData.client_ids[i]]; + for (var j = 0; j < parsedData.client_ids.length; j++) { + delete requests[parsedData.client_ids[j]]; } self.setState({requests: requests}); }, diff --git a/web/react/components/file_upload_overlay.jsx b/web/react/components/file_upload_overlay.jsx new file mode 100644 index 000000000..f35556371 --- /dev/null +++ b/web/react/components/file_upload_overlay.jsx @@ -0,0 +1,26 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +module.exports = React.createClass({ + displayName: 'FileUploadOverlay', + propTypes: { + overlayType: React.PropTypes.string + }, + render: function() { + var overlayClass = 'file-overlay hidden'; + if (this.props.overlayType === 'right') { + overlayClass += ' right-file-overlay'; + } else if (this.props.overlayType === 'center') { + overlayClass += ' center-file-overlay'; + } + + return ( + <div className={overlayClass}> + <div> + <i className='fa fa-upload'></i> + <span>Drop a file to upload it.</span> + </div> + </div> + ); + } +}); diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx index eba4f06f4..f9eacf094 100644 --- a/web/react/components/login.jsx +++ b/web/react/components/login.jsx @@ -4,64 +4,62 @@ 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 BrowserStore = require('../stores/browser_store.jsx'); var Constants = require('../utils/constants.jsx'); module.exports = React.createClass({ handleSubmit: function(e) { e.preventDefault(); - var state = { } + var state = {}; - var name = this.props.teamName + var name = this.props.teamName; if (!name) { - state.server_error = "Bad team name" + state.serverError = 'Bad team name'; this.setState(state); return; } var email = this.refs.email.getDOMNode().value.trim(); if (!email) { - state.server_error = "An email is required" + state.serverError = 'An email is required'; this.setState(state); return; } var password = this.refs.password.getDOMNode().value.trim(); if (!password) { - state.server_error = "A password is required" + state.serverError = 'A password is required'; this.setState(state); return; } if (!BrowserStore.isLocalStorageSupported()) { - state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing."; + state.serverError = 'This service requires local storage to be enabled. Please enable it or exit private browsing.'; this.setState(state); return; } - state.server_error = ""; + state.serverError = ''; this.setState(state); client.loginByEmail(name, email, password, - function(data) { + function loggedIn(data) { UserStore.setCurrentUser(data); UserStore.setLastEmail(email); - var redirect = utils.getUrlParameter("redirect"); + var redirect = utils.getUrlParameter('redirect'); if (redirect) { window.location.pathname = decodeURIComponent(redirect); } else { window.location.pathname = '/' + name + '/channels/town-square'; } - - }.bind(this), - function(err) { - if (err.message == "Login failed because email address has not been verified") { + }, + function loginFailed(err) { + if (err.message === 'Login failed because email address has not been verified') { window.location.href = '/verify_email?name=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email); return; } - state.server_error = err.message; + state.serverError = err.message; this.valid = false; this.setState(state); }.bind(this) @@ -71,10 +69,13 @@ module.exports = React.createClass({ return { }; }, render: function() { - var server_error = this.state.server_error ? <label className="control-label">{this.state.server_error}</label> : null; - var priorEmail = UserStore.getLastEmail() !== "undefined" ? UserStore.getLastEmail() : "" + var serverError; + if (this.state.serverError) { + serverError = <label className='control-label'>{this.state.serverError}</label>; + } + var priorEmail = UserStore.getLastEmail(); - var emailParam = utils.getUrlParameter("email"); + var emailParam = utils.getUrlParameter('email'); if (emailParam) { priorEmail = decodeURIComponent(emailParam); } @@ -84,50 +85,62 @@ module.exports = React.createClass({ var focusEmail = false; var focusPassword = false; - if (priorEmail != "") { + if (priorEmail !== '') { focusPassword = true; } else { focusEmail = true; } - var auth_services = JSON.parse(this.props.authServices); + var authServices = JSON.parse(this.props.authServices); - var login_message; - if (auth_services.indexOf("gitlab") >= 0) { - login_message = ( - <div className="form-group form-group--small"> - <span><a href={"/"+teamName+"/login/gitlab"}>{"Log in with GitLab"}</a></span> + var loginMessage = []; + if (authServices.indexOf(Constants.GITLAB_SERVICE) >= 0) { + loginMessage.push( + <div className='form-group form-group--small'> + <span><a href={'/' + teamName + '/login/gitlab'}>{'Log in with GitLab'}</a></span> </div> ); } + if (authServices.indexOf(Constants.GOOGLE_SERVICE) >= 0) { + loginMessage.push( + <div className='form-group form-group--small'> + <span><a href={'/' + teamName + '/login/google'}>{'Log in with Google'}</a></span> + </div> + ); + } + + var errorClass = ''; + if (serverError) { + errorClass = ' has-error'; + } return ( - <div className="signup-team__container"> - <h5 className="margin--less">Sign in to:</h5> - <h2 className="signup-team__name">{ teamDisplayName }</h2> - <h2 className="signup-team__subdomain">on { config.SiteName }</h2> + <div className='signup-team__container'> + <h5 className='margin--less'>Sign in to:</h5> + <h2 className='signup-team__name'>{teamDisplayName}</h2> + <h2 className='signup-team__subdomain'>on {config.SiteName}</h2> <form onSubmit={this.handleSubmit}> - <div className={server_error ? 'form-group has-error' : 'form-group'}> - { server_error } + <div className={'form-group' + errorClass}> + {serverError} </div> - <div className={server_error ? 'form-group has-error' : 'form-group'}> - <input autoFocus={focusEmail} type="email" className="form-control" name="email" defaultValue={priorEmail} ref="email" placeholder="Email" /> + <div className={'form-group' + errorClass}> + <input autoFocus={focusEmail} type='email' className='form-control' name='email' defaultValue={priorEmail} ref='email' placeholder='Email' /> </div> - <div className={server_error ? 'form-group has-error' : 'form-group'}> - <input autoFocus={focusPassword} type="password" className="form-control" name="password" ref="password" placeholder="Password" /> + <div className={'form-group' + errorClass}> + <input autoFocus={focusPassword} type='password' className='form-control' name='password' ref='password' placeholder='Password' /> </div> - <div className="form-group"> - <button type="submit" className="btn btn-primary">Sign in</button> + <div className='form-group'> + <button type='submit' className='btn btn-primary'>Sign in</button> </div> - { login_message } - <div className="form-group margin--extra form-group--small"> - <span><a href="/find_team">{"Find other " + strings.TeamPlural}</a></span> + {loginMessage} + <div className='form-group margin--extra form-group--small'> + <span><a href='/find_team'>{'Find other ' + strings.TeamPlural}</a></span> </div> - <div className="form-group"> - <a href={"/" + teamName + "/reset_password"}>I forgot my password</a> + <div className='form-group'> + <a href={'/' + teamName + '/reset_password'}>I forgot my password</a> </div> - <div className="margin--extra"> - <span>{"Want to create your own " + strings.Team + "?"} <a href="/" className="signup-team-login">Sign up now</a></span> + <div className='margin--extra'> + <span>{'Want to create your own ' + strings.Team + '?'} <a href='/' className='signup-team-login'>Sign up now</a></span> </div> </form> </div> diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index e72a2d001..f099c67ab 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -11,12 +11,6 @@ var ActionTypes = Constants.ActionTypes; module.exports = React.createClass({ displayName: "Post", - componentDidMount: function() { - $('.modal').on('show.bs.modal', function () { - $('.modal-body').css('overflow-y', 'auto'); - $('.modal-body').css('max-height', $(window).height() * 0.7); - }); - }, handleCommentClick: function(e) { e.preventDefault(); diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 35d43314b..8c76eb82c 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -72,6 +72,12 @@ module.exports = React.createClass({ this.oldScrollHeight = post_holder.scrollHeight; this.oldZoom = (window.outerWidth - 8) / window.innerWidth; + $('.modal').on('show.bs.modal', function () { + $('.modal-body').css('overflow-y', 'auto'); + $('.modal-body').css('max-height', $(window).height() * 0.7); + }); + + // Timeout exists for the DOM to fully render before making changes var self = this; $(window).resize(function(){ $(post_holder).perfectScrollbar('update'); @@ -143,6 +149,7 @@ module.exports = React.createClass({ UserStore.removeStatusesChangeListener(this._onTimeChange); SocketStore.removeChangeListener(this._onSocketChange); $('body').off('click.userpopover'); + $('.modal').off('show.bs.modal') }, resize: function() { var post_holder = $(".post-list-holder-by-time")[0]; diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index ad8b54012..e46979ff7 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -11,6 +11,7 @@ var SearchBox =require('./search_bar.jsx'); var CreateComment = require( './create_comment.jsx' ); var Constants = require('../utils/constants.jsx'); var FileAttachmentList = require('./file_attachment_list.jsx'); +var FileUploadOverlay = require('./file_upload_overlay.jsx'); var ActionTypes = Constants.ActionTypes; RhsHeaderPost = React.createClass({ @@ -296,6 +297,8 @@ module.exports = React.createClass({ return ( <div className="post-right__container"> + <FileUploadOverlay + overlayType='right' /> <div className="search-bar__container sidebar--right__search-header">{searchForm}</div> <div className="sidebar-right__body"> <RhsHeaderPost fromSearch={this.props.fromSearch} isMentionSearch={this.props.isMentionSearch} /> diff --git a/web/react/components/setting_item_min.jsx b/web/react/components/setting_item_min.jsx index 2209c74d1..3c87e416e 100644 --- a/web/react/components/setting_item_min.jsx +++ b/web/react/components/setting_item_min.jsx @@ -2,12 +2,23 @@ // See License.txt for license information. module.exports = React.createClass({ + displayName: 'SettingsItemMin', + propTypes: { + title: React.PropTypes.string, + disableOpen: React.PropTypes.bool, + updateSection: React.PropTypes.func, + describe: React.PropTypes.string + }, render: function() { + var editButton = ''; + if (!this.props.disableOpen) { + editButton = <li className='col-sm-2 section-edit'><a className='section-edit theme' href='#' onClick={this.props.updateSection}>Edit</a></li>; + } return ( - <ul className="section-min"> - <li className="col-sm-10 section-title">{this.props.title}</li> - <li className="col-sm-2 section-edit"><a className="section-edit theme" href="#" onClick={this.props.updateSection}>Edit</a></li> - <li className="col-sm-7 section-describe">{this.props.describe}</li> + <ul className='section-min'> + <li className='col-sm-10 section-title'>{this.props.title}</li> + {editButton} + <li className='col-sm-7 section-describe'>{this.props.describe}</li> </ul> ); } diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index cf6da7984..6735bd6e5 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -128,6 +128,7 @@ module.exports = React.createClass({ ChannelStore.addChangeListener(this.onChange); UserStore.addChangeListener(this.onChange); UserStore.addStatusesChangeListener(this.onChange); + TeamStore.addChangeListener(this.onChange); SocketStore.addChangeListener(this.onSocketChange); $('.nav-pills__container').perfectScrollbar(); @@ -146,6 +147,7 @@ module.exports = React.createClass({ ChannelStore.removeChangeListener(this.onChange); UserStore.removeChangeListener(this.onChange); UserStore.removeStatusesChangeListener(this.onChange); + TeamStore.removeChangeListener(this.onChange); SocketStore.removeChangeListener(this.onSocketChange); }, onChange: function() { @@ -348,15 +350,16 @@ module.exports = React.createClass({ // set up click handler to switch channels (or create a new channel for non-existant ones) var clickHandler = null; - var href; + var href = '#'; + var teamURL = TeamStore.getCurrentTeamUrl(); if (!channel.fake) { clickHandler = function(e) { e.preventDefault(); utils.switchChannel(channel); }; - href = '#'; - } else { - href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name; + } + if (channel.fake && teamURL){ + href = teamURL + '/channels/' + channel.name; } return ( diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx index b21553d8a..0393e0413 100644 --- a/web/react/components/signup_user_complete.jsx +++ b/web/react/components/signup_user_complete.jsx @@ -5,6 +5,7 @@ var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); var UserStore = require('../stores/user_store.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); +var Constants = require('../utils/constants.jsx'); module.exports = React.createClass({ handleSubmit: function(e) { @@ -151,19 +152,34 @@ module.exports = React.createClass({ // 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> + var signupMessage = []; + if (authServices.indexOf(Constants.GITLAB_SERVICE) >= 0) { + signupMessage.push( <a className='btn btn-custom-login gitlab' href={'/' + this.props.teamName + '/signup/gitlab' + window.location.search}> <span className='icon' /> <span>with GitLab</span> </a> + ); + } + + if (authServices.indexOf(Constants.GOOGLE_SERVICE) >= 0) { + signupMessage.push( + <a className='btn btn-custom-login google' href={'/' + this.props.teamName + '/signup/google' + window.location.search}> + <span className='icon' /> + <span>with Google</span> + </a> + ); + } + + if (signupMessage.length > 0) { + signupMessage = ( + <div> + {signupMessage} <div className='or__container'> <span>or</span> </div> - </div> - ); + </div> + ); } var termsDisclaimer = null; diff --git a/web/react/components/signup_user_oauth.jsx b/web/react/components/signup_user_oauth.jsx index 6322aedee..8b2800bde 100644 --- a/web/react/components/signup_user_oauth.jsx +++ b/web/react/components/signup_user_oauth.jsx @@ -33,7 +33,10 @@ module.exports = React.createClass({ client.createUser(user, "", "", function(data) { client.track('signup', 'signup_user_oauth_02'); - window.location.href = '/' + this.props.teamName + '/login/'+user.auth_service; + UserStore.setCurrentUser(data); + UserStore.setLastEmail(data.email); + + window.location.href = '/' + this.props.teamName + '/login/' + user.auth_service + '?login_hint=' + user.email; }.bind(this), function(err) { this.state.server_error = err.message; diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index a5fa01dc9..8f29bbe57 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -13,6 +13,7 @@ var assign = require('object-assign'); function getNotificationsStateFromStores() { var user = UserStore.getCurrentUser(); + var soundNeeded = !utils.isBrowserFirefox(); var sound = (!user.notify_props || user.notify_props.desktop_sound == undefined) ? "true" : user.notify_props.desktop_sound; var desktop = (!user.notify_props || user.notify_props.desktop == undefined) ? "all" : user.notify_props.desktop; var email = (!user.notify_props || user.notify_props.email == undefined) ? "true" : user.notify_props.email; @@ -58,7 +59,7 @@ function getNotificationsStateFromStores() { } } - return { notify_level: desktop, enable_email: email, enable_sound: sound, username_key: username_key, mention_key: mention_key, custom_keys: custom_keys, custom_keys_checked: custom_keys.length > 0, first_name_key: first_name_key, all_key: all_key, channel_key: channel_key }; + return { notify_level: desktop, enable_email: email, soundNeeded: soundNeeded, enable_sound: sound, username_key: username_key, mention_key: mention_key, custom_keys: custom_keys, custom_keys_checked: custom_keys.length > 0, first_name_key: first_name_key, all_key: all_key, channel_key: channel_key }; } @@ -235,7 +236,7 @@ var NotificationsTab = React.createClass({ } var soundSection; - if (this.props.activeSection === 'sound') { + if (this.props.activeSection === 'sound' && this.state.soundNeeded) { var soundActive = ["",""]; if (this.state.enable_sound === "false") { soundActive[1] = "active"; @@ -265,7 +266,9 @@ var NotificationsTab = React.createClass({ ); } else { var describe = ""; - if (this.state.enable_sound === "false") { + if (!this.state.soundNeeded) { + describe = "Please configure notification sounds in your browser settings" + } else if (this.state.enable_sound === "false") { describe = "Off"; } else { describe = "On"; @@ -276,6 +279,7 @@ var NotificationsTab = React.createClass({ title="Desktop notification sounds" describe={describe} updateSection={function(){self.props.updateSection("sound");}} + disableOpen = {!this.state.soundNeeded} /> ); } |