summaryrefslogtreecommitdiffstats
path: root/web/react/components
diff options
context:
space:
mode:
Diffstat (limited to 'web/react/components')
-rw-r--r--web/react/components/channel_header.jsx2
-rw-r--r--web/react/components/channel_loader.jsx1
-rw-r--r--web/react/components/confirm_modal.jsx31
-rw-r--r--web/react/components/create_comment.jsx2
-rw-r--r--web/react/components/create_post.jsx6
-rw-r--r--web/react/components/invite_member_modal.jsx130
-rw-r--r--web/react/components/login.jsx14
-rw-r--r--web/react/components/navbar.jsx2
-rw-r--r--web/react/components/post_body.jsx16
-rw-r--r--web/react/components/settings_sidebar.jsx13
-rw-r--r--web/react/components/sidebar_header.jsx3
-rw-r--r--web/react/components/sidebar_right_menu.jsx2
-rw-r--r--web/react/components/signup_team_complete.jsx13
-rw-r--r--web/react/components/signup_user_complete.jsx10
-rw-r--r--web/react/components/team_settings.jsx161
-rw-r--r--web/react/components/team_settings_modal.jsx (renamed from web/react/components/settings_modal.jsx)14
-rw-r--r--web/react/components/user_settings.jsx9
-rw-r--r--web/react/components/user_settings_modal.jsx68
-rw-r--r--web/react/components/view_image.jsx2
19 files changed, 417 insertions, 82 deletions
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 006c168ba..ade58a10a 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -201,7 +201,7 @@ module.exports = React.createClass({
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_info" data-channelid={this.state.channel.id} href="#">View Info</a></li>
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Invite Members</a></li>
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
{ isAdmin ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
: ""
diff --git a/web/react/components/channel_loader.jsx b/web/react/components/channel_loader.jsx
index 5252f275c..537a41d03 100644
--- a/web/react/components/channel_loader.jsx
+++ b/web/react/components/channel_loader.jsx
@@ -18,6 +18,7 @@ module.exports = React.createClass({
AsyncClient.getChannelExtraInfo(true);
AsyncClient.findTeams();
AsyncClient.getStatuses();
+ AsyncClient.getMyTeam();
/* End of async loads */
diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx
new file mode 100644
index 000000000..3be13cf9b
--- /dev/null
+++ b/web/react/components/confirm_modal.jsx
@@ -0,0 +1,31 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+module.exports = React.createClass({
+ handleConfirm: function() {
+ $('#'+this.props.parent_id).attr('data-confirm', 'true');
+ $('#'+this.props.parent_id).modal('hide');
+ $('#'+this.props.id).modal('hide');
+ },
+ render: function() {
+ return (
+ <div className="modal fade" id={this.props.id} tabIndex="-1" role="dialog" aria-hidden="true">
+ <div className="modal-dialog">
+ <div className="modal-content">
+ <div className="modal-header">
+ <h4 className="modal-title">{this.props.title}</h4>
+ </div>
+ <div className="modal-body">
+ {this.props.message}
+ </div>
+ <div className="modal-footer">
+ <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button>
+ <button onClick={this.handleConfirm} type="button" className="btn btn-primary">{this.props.confirm_button}</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+});
+
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 9bcbad079..cb7aa371c 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -145,7 +145,7 @@ module.exports = React.createClass({
onUserInput={this.handleUserInput}
onKeyPress={this.commentMsgKeyPress}
messageText={this.state.messageText}
- createMessage="Create a comment..."
+ createMessage="Add a comment..."
initialText=""
id="reply_textbox"
ref="textbox" />
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 191be9bf8..5a0b6f85f 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -31,9 +31,7 @@ module.exports = React.createClass({
post.message = this.state.messageText;
- var repRegex = new RegExp("<br>", "g");
- if (post.message.replace(repRegex, " ").trim().length === 0
- && this.state.previews.length === 0) {
+ if (post.message.trim().length === 0 && this.state.previews.length === 0) {
return;
}
@@ -250,7 +248,7 @@ module.exports = React.createClass({
onUserInput={this.handleUserInput}
onKeyPress={this.postMsgKeyPress}
messageText={this.state.messageText}
- createMessage="Create a post..."
+ createMessage="Write a message..."
channelId={this.state.channel_id}
id="post_textbox"
ref="textbox" />
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index 1d2bbed84..1854107f8 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -4,8 +4,37 @@
var utils = require('../utils/utils.jsx');
var Client =require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
+var ConfirmModal = require('./confirm_modal.jsx');
module.exports = React.createClass({
+ componentDidMount: function() {
+ var self = this;
+ $('#invite_member').on('hide.bs.modal', function(e) {
+ if ($('#invite_member').attr('data-confirm') === 'true') {
+ $('#invite_member').attr('data-confirm', 'false');
+ return;
+ }
+
+ var not_empty = false;
+ for (var i = 0; i < self.state.invite_ids.length; i++) {
+ var index = self.state.invite_ids[i];
+ if (self.refs["email"+index].getDOMNode().value.trim() !== '') {
+ not_empty = true;
+ break;
+ }
+ }
+
+ if (not_empty) {
+ $('#confirm_invite_modal').modal('show');
+ e.preventDefault();
+ }
+
+ });
+
+ $('#invite_member').on('hidden.bs.modal', function() {
+ self.clearFields();
+ });
+ },
handleSubmit: function(e) {
var invite_ids = this.state.invite_ids;
var count = invite_ids.length;
@@ -56,22 +85,8 @@ module.exports = React.createClass({
Client.inviteMembers(data,
function() {
+ $(this.refs.modal.getDOMNode()).attr('data-confirm', 'true');
$(this.refs.modal.getDOMNode()).modal('hide');
- for (var i = 0; i < invite_ids.length; i++) {
- var index = invite_ids[i];
- this.refs["email"+index].getDOMNode().value = "";
- if (config.AllowInviteNames) {
- this.refs["first_name"+index].getDOMNode().value = "";
- this.refs["last_name"+index].getDOMNode().value = "";
- }
- }
- this.setState({
- invite_ids: [0],
- id_count: 0,
- email_errors: {},
- first_name_errors: {},
- last_name_errors: {}
- });
}.bind(this),
function(err) {
this.setState({ server_error: err });
@@ -89,11 +104,33 @@ module.exports = React.createClass({
invite_ids.push(count);
this.setState({ invite_ids: invite_ids, id_count: count });
},
+ clearFields: function() {
+ var invite_ids = this.state.invite_ids;
+
+ for (var i = 0; i < invite_ids.length; i++) {
+ var index = invite_ids[i];
+ this.refs["email"+index].getDOMNode().value = "";
+ if (config.AllowInviteNames) {
+ this.refs["first_name"+index].getDOMNode().value = "";
+ this.refs["last_name"+index].getDOMNode().value = "";
+ }
+ }
+
+ this.setState({
+ invite_ids: [0],
+ id_count: 0,
+ email_errors: {},
+ first_name_errors: {},
+ last_name_errors: {}
+ });
+ },
removeInviteFields: function(index) {
+ var count = this.state.id_count;
var invite_ids = this.state.invite_ids;
var i = invite_ids.indexOf(index);
- if (index > -1) invite_ids.splice(i, 1);
- this.setState({ invite_ids: invite_ids });
+ if (i > -1) invite_ids.splice(i, 1);
+ if (!invite_ids.length) invite_ids.push(++count);
+ this.setState({ invite_ids: invite_ids, id_count: count });
},
getInitialState: function() {
return {
@@ -119,11 +156,9 @@ module.exports = React.createClass({
invite_sections[index] = (
<div key={"key" + index}>
- { i ?
<div>
- <button type="button" className="btn remove__member" onClick={function(){self.removeInviteFields(index);}}>×</button>
+ <button type="button" className="btn remove__member" onClick={this.removeInviteFields.bind(this, index)}>×</button>
</div>
- : ""}
<div className={ email_error ? "form-group invite has-error" : "form-group invite" }>
<input onKeyUp={this.displayNameKeyUp} type="text" ref={"email"+index} className="form-control" placeholder="email@domain.com" maxLength="64" />
{ email_error }
@@ -147,29 +182,38 @@ module.exports = React.createClass({
var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null;
return (
- <div className="modal fade" ref="modal" id="invite_member" tabIndex="-1" role="dialog" aria-hidden="true">
- <div className="modal-dialog">
- <div className="modal-content">
- <div className="modal-header">
- <button type="button" className="close" data-dismiss="modal" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button>
- <h4 className="modal-title" id="myModalLabel">Invite New Member</h4>
- </div>
- <div ref="modalBody" className="modal-body">
- <form role="form">
- { invite_sections }
- </form>
- { server_error }
- <button type="button" className="btn btn-default" onClick={this.addInviteFields}>Add another</button>
- <br/>
- <br/>
- <label className='control-label'>People invited automatically join Town Square channel.</label>
- </div>
- <div className="modal-footer">
- <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
- <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Send Invitations</button>
- </div>
- </div>
- </div>
+ <div>
+ <div className="modal fade" ref="modal" id="invite_member" tabIndex="-1" role="dialog" aria-hidden="true">
+ <div className="modal-dialog">
+ <div className="modal-content">
+ <div className="modal-header">
+ <button type="button" className="close" data-dismiss="modal" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button>
+ <h4 className="modal-title" id="myModalLabel">Invite New Member</h4>
+ </div>
+ <div ref="modalBody" className="modal-body">
+ <form role="form">
+ { invite_sections }
+ </form>
+ { server_error }
+ <button type="button" className="btn btn-default" onClick={this.addInviteFields}>Add another</button>
+ <br/>
+ <br/>
+ <label className='control-label'>People invited automatically join Town Square channel.</label>
+ </div>
+ <div className="modal-footer">
+ <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
+ <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Send Invitations</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <ConfirmModal
+ id="confirm_invite_modal"
+ parent_id="invite_member"
+ title="Discard Invitations?"
+ message="You have unsent invitations, are you sure you want to discard them?"
+ confirm_button="Yes, Discard"
+ />
</div>
);
} else {
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index 65f1da1f8..85df5f797 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -21,6 +21,12 @@ var FindTeamDomain = React.createClass({
return;
}
+ if (!utils.isLocalStorageSupported()) {
+ state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing.";
+ this.setState(state);
+ return;
+ }
+
state.server_error = "";
this.setState(state);
@@ -94,7 +100,7 @@ module.exports = React.createClass({
return;
}
- var email = this.refs.email.getDOMNode().value.trim();
+ var email = this.refs.email.getDOMNode().value.trim();
if (!email) {
state.server_error = "An email is required"
this.setState(state);
@@ -108,6 +114,12 @@ module.exports = React.createClass({
return;
}
+ if (!utils.isLocalStorageSupported()) {
+ state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing.";
+ this.setState(state);
+ return;
+ }
+
state.server_error = "";
this.setState(state);
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index 3821c2772..35f7d9044 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -301,7 +301,7 @@ module.exports = React.createClass({
<span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Invite Members</a></li>
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
{ isAdmin ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
: ""
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index 55fc32c33..3079917ec 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -85,20 +85,22 @@ module.exports = React.createClass({
var postFiles = [];
var images = [];
if (filenames) {
- for (var i = 0; i < filenames.length && i < Constants.MAX_DISPLAY_FILES; i++) {
+ for (var i = 0; i < filenames.length; i++) {
var fileInfo = utils.splitFileLocation(filenames[i]);
if (Object.keys(fileInfo).length === 0) continue;
var type = utils.getFileType(fileInfo.ext);
if (type === "image") {
- postFiles.push(
- <div className="post-image__column" key={filenames[i]}>
- <a href="#" onClick={this.handleImageClick} data-img-id={images.length.toString()} data-toggle="modal" data-target={"#" + postImageModalId }><div ref={filenames[i]} className="post__load" style={{backgroundImage: 'url(/static/images/load.gif)'}}></div></a>
- </div>
- );
+ if (i < Constants.MAX_DISPLAY_FILES) {
+ postFiles.push(
+ <div className="post-image__column" key={filenames[i]}>
+ <a href="#" onClick={this.handleImageClick} data-img-id={images.length.toString()} data-toggle="modal" data-target={"#" + postImageModalId }><div ref={filenames[i]} className="post__load" style={{backgroundImage: 'url(/static/images/load.gif)'}}></div></a>
+ </div>
+ );
+ }
images.push(filenames[i]);
- } else {
+ } else if (i < Constants.MAX_DISPLAY_FILES) {
postFiles.push(
<div className="post-image__column custom-file" key={fileInfo.name}>
<a href={fileInfo.path+"."+fileInfo.ext} download={fileInfo.name+"."+fileInfo.ext}>
diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx
index a1546890f..ae8510cf2 100644
--- a/web/react/components/settings_sidebar.jsx
+++ b/web/react/components/settings_sidebar.jsx
@@ -1,6 +1,8 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
+var utils = require('../utils/utils.jsx');
+
module.exports = React.createClass({
updateTab: function(tab) {
this.props.updateTab(tab);
@@ -11,16 +13,11 @@ module.exports = React.createClass({
return (
<div className="">
<ul className="nav nav-pills nav-stacked">
- <li className={this.props.activeTab == 'general' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("general");}}><i className="glyphicon glyphicon-cog"></i>General</a></li>
- <li className={this.props.activeTab == 'security' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("security");}}><i className="glyphicon glyphicon-lock"></i>Security</a></li>
- <li className={this.props.activeTab == 'notifications' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("notifications");}}><i className="glyphicon glyphicon-exclamation-sign"></i>Notifications</a></li>
- <li className={this.props.activeTab == 'appearance' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("appearance");}}><i className="glyphicon glyphicon-wrench"></i>Appearance</a></li>
+ {this.props.tabs.map(function(tab) {
+ return <li className={self.props.activeTab == tab.name ? 'active' : ''}><a href="#" onClick={function(){self.updateTab(tab.name);}}><i className={tab.icon}></i>{tab.ui_name}</a></li>
+ })}
</ul>
</div>
);
- /* Temporarily removing sessions and activity logs
- <li className={this.props.activeTab == 'sessions' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("sessions");}}><i className="glyphicon glyphicon-globe"></i>Sessions</a></li>
- <li className={this.props.activeTab == 'activity_log' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("activity_log");}}><i className="glyphicon glyphicon-time"></i>Activity Log</a></li>
- */
}
});
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index 5a872b7a0..0b59d2036 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -94,7 +94,8 @@ var NavbarDropdown = React.createClass({
<i className="dropdown__icon"></i>
</a>
<ul className="dropdown-menu" role="menu">
- <li><a href="#" data-toggle="modal" data-target="#settings_modal">Account Settings</a></li>
+ <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> : "" }
{ invite_link }
{ team_link }
{ manage_link }
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index d0c139d1a..c523ce554 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -59,7 +59,7 @@ module.exports = React.createClass({
<div className="nav-pills__container">
<ul className="nav nav-pills nav-stacked">
- <li><a href="#" data-toggle="modal" data-target="#settings_modal"><i className="glyphicon glyphicon-cog"></i>Account Settings</a></li>
+ <li><a href="#" data-toggle="modal" data-target="#user_settings1"><i className="glyphicon glyphicon-cog"></i>Account Settings</a></li>
{ invite_link }
{ team_link }
{ manage_link }
diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx
index b038679e6..99dcf6f5f 100644
--- a/web/react/components/signup_team_complete.jsx
+++ b/web/react/components/signup_team_complete.jsx
@@ -9,6 +9,10 @@ var constants = require('../utils/constants.jsx')
WelcomePage = React.createClass({
submitNext: function (e) {
+ if (!utils.isLocalStorageSupported()) {
+ this.setState({ storage_error: "This service requires local storage to be enabled. Please enable it or exit private browsing."} );
+ return;
+ }
e.preventDefault();
this.props.state.wizard = "team_name";
this.props.updateParent(this.props.state);
@@ -26,6 +30,12 @@ WelcomePage = React.createClass({
if (!email || !utils.isEmail(email)) {
state.email_error = "Please enter a valid email address";
this.setState(state);
+ return;
+ }
+ else if (!utils.isLocalStorageSupported()) {
+ state.email_error = "This service requires local storage to be enabled. Please enable it or exit private browsing.";
+ this.setState(state);
+ return;
}
else {
state.email_error = "";
@@ -50,6 +60,7 @@ WelcomePage = React.createClass({
client.track('signup', 'signup_team_01_welcome');
+ var storage_error = this.state.storage_error ? <label className="control-label">{ this.state.storage_error }</label> : null;
var email_error = this.state.email_error ? <label className="control-label">{ this.state.email_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;
@@ -66,6 +77,7 @@ WelcomePage = React.createClass({
</p>
<div className="form-group">
<button className="btn-primary btn form-group" onClick={this.submitNext}><i className="glyphicon glyphicon-ok"></i>Yes, this address is correct</button>
+ { storage_error }
</div>
<hr />
<p>If this is not correct, you can switch to a different email. We'll send you a new invite right away.</p>
@@ -491,6 +503,7 @@ PasswordPage = React.createClass({
return;
}
+ this.setState({name_error: ""});
$('#finish-button').button('loading');
var teamSignup = JSON.parse(JSON.stringify(this.props.state));
teamSignup.user.password = password;
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index 146419cf5..b9f32f0bc 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -13,16 +13,16 @@ module.exports = React.createClass({
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: ""});
+ this.setState({name_error: "This field is required", email_error: "", password_error: "", server_error: ""});
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." });
+ this.setState({name_error: "This username is reserved, please choose a new one.", email_error: "", password_error: "", server_error: ""});
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 '_'." });
+ 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: ""});
return;
}
@@ -34,10 +34,12 @@ module.exports = React.createClass({
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"});
+ this.setState({name_error: "", email_error: "", password_error: "Please enter at least 5 characters", server_error: ""});
return;
}
+ this.setState({name_error: "", email_error: "", password_error: "", server_error: ""});
+
this.state.user.allow_marketing = this.refs.email_service.getDOMNode().checked;
client.createUser(this.state.user, this.state.data, this.state.hash,
diff --git a/web/react/components/team_settings.jsx b/web/react/components/team_settings.jsx
new file mode 100644
index 000000000..0cec30f3e
--- /dev/null
+++ b/web/react/components/team_settings.jsx
@@ -0,0 +1,161 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var UserStore = require('../stores/user_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
+var SettingItemMin = require('./setting_item_min.jsx');
+var SettingItemMax = require('./setting_item_max.jsx');
+var SettingPicture = require('./setting_picture.jsx');
+var utils = require('../utils/utils.jsx');
+
+var client = require('../utils/client.jsx');
+var AsyncClient = require('../utils/async_client.jsx');
+var Constants = require('../utils/constants.jsx');
+
+var FeatureTab = React.createClass({
+ submitValetFeature: function() {
+ data = {};
+ data['allow_valet'] = this.state.allow_valet;
+
+ client.updateValetFeature(data,
+ function(data) {
+ this.props.updateSection("");
+ AsyncClient.getMyTeam();
+ }.bind(this),
+ function(err) {
+ state = this.getInitialState();
+ state.server_error = err;
+ this.setState(state);
+ }.bind(this)
+ );
+ },
+ handleValetRadio: function(val) {
+ this.setState({ allow_valet: val });
+ this.refs.wrapper.getDOMNode().focus();
+ },
+ componentWillReceiveProps: function(newProps) {
+ var team = newProps.team;
+
+ var allow_valet = "false";
+ if (team && team.allow_valet) {
+ allow_valet = "true";
+ }
+
+ this.setState({ allow_valet: allow_valet });
+ },
+ getInitialState: function() {
+ var team = this.props.team;
+
+ var allow_valet = "false";
+ if (team && team.allow_valet) {
+ allow_valet = "true";
+ }
+
+ return { allow_valet: allow_valet };
+ },
+ render: function() {
+ var team = this.props.team;
+
+ 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 valetSection;
+ var self = this;
+
+ if (this.props.activeSection === 'valet') {
+ var valetActive = ["",""];
+ if (this.state.allow_valet === "false") {
+ valetActive[1] = "active";
+ } else {
+ valetActive[0] = "active";
+ }
+
+ var inputs = [];
+
+ inputs.push(
+ <div className="col-sm-12">
+ <div className="btn-group" data-toggle="buttons-radio">
+ <button className={"btn btn-default "+valetActive[0]} onClick={function(){self.handleValetRadio("true")}}>On</button>
+ <button className={"btn btn-default "+valetActive[1]} onClick={function(){self.handleValetRadio("false")}}>Off</button>
+ </div>
+ <div><br/>Warning: Turning on the Valet feature and using it with any third party software increases the risk of a security breach.</div>
+ </div>
+ );
+
+ valetSection = (
+ <SettingItemMax
+ title="Valet"
+ inputs={inputs}
+ submit={this.submitValetFeature}
+ server_error={server_error}
+ client_error={client_error}
+ updateSection={function(e){self.props.updateSection("");e.preventDefault();}}
+ />
+ );
+ } else {
+ var describe = "";
+ if (this.state.allow_valet === "false") {
+ describe = "Off";
+ } else {
+ describe = "On";
+ }
+
+ valetSection = (
+ <SettingItemMin
+ title="Valet"
+ describe={describe}
+ updateSection={function(){self.props.updateSection("valet");}}
+ />
+ );
+ }
+
+ return (
+ <div>
+ <div className="modal-header">
+ <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 className="modal-title" ref="title"><i className="modal-back"></i>General Settings</h4>
+ </div>
+ <div ref="wrapper" className="user-settings">
+ <h3 className="tab-header">Feature Settings</h3>
+ <div className="divider-dark first"/>
+ {valetSection}
+ <div className="divider-dark"/>
+ </div>
+ </div>
+ );
+ }
+});
+
+module.exports = React.createClass({
+ componentDidMount: function() {
+ TeamStore.addChangeListener(this._onChange);
+ },
+ componentWillUnmount: function() {
+ TeamStore.removeChangeListener(this._onChange);
+ },
+ _onChange: function () {
+ var team = TeamStore.getCurrent();
+ if (!utils.areStatesEqual(this.state.team, team)) {
+ this.setState({ team: team });
+ }
+ },
+ getInitialState: function() {
+ return { team: TeamStore.getCurrent() };
+ },
+ render: function() {
+ if (this.props.activeTab === 'general') {
+ return (
+ <div>
+ </div>
+ );
+ } else if (this.props.activeTab === 'feature') {
+ return (
+ <div>
+ <FeatureTab team={this.state.team} activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
+ </div>
+ );
+ } else {
+ return <div/>;
+ }
+ }
+});
diff --git a/web/react/components/settings_modal.jsx b/web/react/components/team_settings_modal.jsx
index 57a869f93..08a952d2e 100644
--- a/web/react/components/settings_modal.jsx
+++ b/web/react/components/team_settings_modal.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
var SettingsSidebar = require('./settings_sidebar.jsx');
-var UserSettings = require('./user_settings.jsx');
+var TeamSettings = require('./team_settings.jsx');
module.exports = React.createClass({
componentDidMount: function() {
@@ -22,27 +22,31 @@ module.exports = React.createClass({
this.setState({ active_section: section });
},
getInitialState: function() {
- return { active_tab: "general", active_section: "" };
+ return { active_tab: "feature", active_section: "" };
},
render: function() {
+ var tabs = [];
+ tabs.push({name: "feature", ui_name: "Features", icon: "glyphicon glyphicon-wrench"});
+
return (
- <div className="modal fade" ref="modal" id="settings_modal" role="dialog" aria-hidden="true">
+ <div className="modal fade" ref="modal" id="team_settings" role="dialog" aria-hidden="true">
<div className="modal-dialog settings-modal">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- <h4 className="modal-title" ref="title">Account Settings</h4>
+ <h4 className="modal-title" ref="title">Team Settings</h4>
</div>
<div className="modal-body">
<div className="settings-table">
<div className="settings-links">
<SettingsSidebar
+ tabs={tabs}
activeTab={this.state.active_tab}
updateTab={this.updateTab}
/>
</div>
<div className="settings-content">
- <UserSettings
+ <TeamSettings
activeTab={this.state.active_tab}
activeSection={this.state.active_section}
updateSection={this.updateSection}
diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx
index a9c2433f2..7d542a8b7 100644
--- a/web/react/components/user_settings.jsx
+++ b/web/react/components/user_settings.jsx
@@ -658,9 +658,10 @@ var SecurityTab = React.createClass({
);
} else {
var d = new Date(this.props.user.last_password_update);
- var hour = d.getHours() < 10 ? "0" + d.getHours() : String(d.getHours());
+ var hour = d.getHours() % 12 ? String(d.getHours() % 12) : "12";
var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes());
- var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min;
+ var timeOfDay = d.getHours() >= 12 ? " pm" : " am";
+ var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min + timeOfDay;
passwordSection = (
<SettingItemMin
@@ -1056,7 +1057,7 @@ var AppearanceTab = React.createClass({
themeSection = (
<SettingItemMax
- title="Theme"
+ title="Theme Color"
inputs={inputs}
submit={this.submitTheme}
server_error={server_error}
@@ -1066,7 +1067,7 @@ var AppearanceTab = React.createClass({
} else {
themeSection = (
<SettingItemMin
- title="Theme"
+ title="Theme Color"
describe={this.state.theme}
updateSection={function(){self.props.updateSection("theme");}}
/>
diff --git a/web/react/components/user_settings_modal.jsx b/web/react/components/user_settings_modal.jsx
new file mode 100644
index 000000000..ff001611d
--- /dev/null
+++ b/web/react/components/user_settings_modal.jsx
@@ -0,0 +1,68 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var SettingsSidebar = require('./settings_sidebar.jsx');
+var UserSettings = require('./user_settings.jsx');
+
+module.exports = React.createClass({
+ componentDidMount: function() {
+ $('body').on('click', '.modal-back', function(){
+ $(this).closest('.modal-dialog').removeClass('display--content');
+ });
+ $('body').on('click', '.modal-header .close', function(){
+ setTimeout(function() {
+ $('.modal-dialog.display--content').removeClass('display--content');
+ }, 500);
+ });
+ },
+ updateTab: function(tab) {
+ this.setState({ active_tab: tab });
+ },
+ updateSection: function(section) {
+ this.setState({ active_section: section });
+ },
+ getInitialState: function() {
+ return { active_tab: "general", active_section: "" };
+ },
+ render: function() {
+ var tabs = [];
+ tabs.push({name: "general", ui_name: "General", icon: "glyphicon glyphicon-cog"});
+ tabs.push({name: "security", ui_name: "Security", icon: "glyphicon glyphicon-lock"});
+ tabs.push({name: "notifications", ui_name: "Notifications", icon: "glyphicon glyphicon-exclamation-sign"});
+ tabs.push({name: "appearance", ui_name: "Appearance", icon: "glyphicon glyphicon-wrench"});
+ //tabs.push({name: "sessions", ui_name: "Sessions", icon: "glyphicon glyphicon-globe"});
+ //tabs.push({name: "activity_log", ui_name: "Activity Log", icon: "glyphicon glyphicon-time"});
+
+ return (
+ <div className="modal fade" ref="modal" id="user_settings1" role="dialog" aria-hidden="true">
+ <div className="modal-dialog settings-modal">
+ <div className="modal-content">
+ <div className="modal-header">
+ <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 className="modal-title" ref="title">Account Settings</h4>
+ </div>
+ <div className="modal-body">
+ <div className="settings-table">
+ <div className="settings-links">
+ <SettingsSidebar
+ tabs={tabs}
+ activeTab={this.state.active_tab}
+ updateTab={this.updateTab}
+ />
+ </div>
+ <div className="settings-content">
+ <UserSettings
+ activeTab={this.state.active_tab}
+ activeSection={this.state.active_section}
+ updateSection={this.updateSection}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+});
+
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
index 4cb30e1d3..c573e9dbb 100644
--- a/web/react/components/view_image.jsx
+++ b/web/react/components/view_image.jsx
@@ -124,7 +124,7 @@ module.exports = React.createClass({
<div key={name+"_loading"}>
<img ref="placeholder" className="loader-image" src="/static/images/load.gif" />
{ percentage > 0 ?
- <span className="loader-percent" >{"Downloading " + percentage + "%"}</span>
+ <span className="loader-percent" >{"Previewing " + percentage + "%"}</span>
: ""}
</div>
);