Этот коммит содержится в:
=Corey Hulen
2015-08-21 08:54:48 -07:00
родитель 60be2a5636 ddcdcc3e2c
Коммит 0ae7180ac5
23 изменённых файлов: 766 добавлений и 314 удалений

Просмотреть файл

@@ -79,18 +79,26 @@ Local Machine Setup (Docker)
3. When docker is done fetching the image, open http://localhost:8065/ in your browser. 3. When docker is done fetching the image, open http://localhost:8065/ in your browser.
### Additional Notes ### ### Additional Notes ###
- If you want to work with the latest bits in the repository (i.e. not a stable release) you can run the cmd: - If you want to work with the latest master from the repository (i.e. not a stable release) you can run the cmd:
`docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:dev`
- You can update to the latest bits by running: ``` bash
`docker pull mattermost/platform:dev` docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:dev
```
- Instructions on how to update your docker image are found below.
- If you wish to remove mattermost-dev use: - If you wish to remove mattermost-dev use:
`docker stop mattermost-dev`
`docker rm -v mattermost-dev` ``` bash
docker stop mattermost-dev
docker rm -v mattermost-dev
```
- If you wish to gain access to a shell on the container use: - If you wish to gain access to a shell on the container use:
`docker exec -ti mattermost-dev /bin/bash`
``` bash
docker exec -ti mattermost-dev /bin/bash
```
AWS Elastic Beanstalk Setup (Docker) AWS Elastic Beanstalk Setup (Docker)
------------------------------------ ------------------------------------
@@ -163,6 +171,35 @@ Email Setup (Optional)
3. The service should restart automatically. Verify the Mattermost service is running with `ps -A` 3. The service should restart automatically. Verify the Mattermost service is running with `ps -A`
4. Current logged in users will not be affected, but upon logging out or session expiration users will be required to verify their email address. 4. Current logged in users will not be affected, but upon logging out or session expiration users will be required to verify their email address.
Upgrading Mattermost
---------------------
### Docker ###
To upgrade your docker image to the latest release (NOTE: this will destroy all data in the docker container):
1. Stop your docker container by running:
``` bash
docker stop mattermost-dev
```
2. Delete your docker container by running:
``` bash
docker rm mattermost-dev
```
3. Update your docker image by running:
``` bash
docker pull mattermost/platform
```
4. Start your docker container by running:
``` bash
docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform
```
To upgrade to the latest master from the repository replace `mattermost/platform` with `mattermost/platform:dev` in the above instructions.
Contributing Contributing
------------ ------------

Просмотреть файл

@@ -5,6 +5,7 @@ package store
import ( import (
"fmt" "fmt"
"strings"
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils" "github.com/mattermost/platform/utils"
) )
@@ -163,6 +164,17 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha
user.EmailVerified = false user.EmailVerified = false
} }
if user.Username != oldUser.Username {
nonUsernameKeys := []string{}
splitKeys := strings.Split(user.NotifyProps["mention_keys"], ",")
for _, key := range splitKeys {
if key != oldUser.Username && key != "@" + oldUser.Username {
nonUsernameKeys = append(nonUsernameKeys, key)
}
}
user.NotifyProps["mention_keys"] = strings.Join(nonUsernameKeys, ",") + user.Username + ",@" + user.Username
}
if count, err := us.GetMaster().Update(user); err != nil { if count, err := us.GetMaster().Update(user); err != nil {
if IsUniqueConstraintError(err.Error(), "Email", "users_email_teamid_key") { if IsUniqueConstraintError(err.Error(), "Email", "users_email_teamid_key") {
result.Err = model.NewAppError("SqlUserStore.Update", "This email is already taken. Please choose another", "user_id="+user.Id+", "+err.Error()) result.Err = model.NewAppError("SqlUserStore.Update", "This email is already taken. Please choose another", "user_id="+user.Id+", "+err.Error())

Просмотреть файл

@@ -105,7 +105,7 @@ module.exports = React.createClass({
if (!utils.areStatesEqual(newState, this.state)) { if (!utils.areStatesEqual(newState, this.state)) {
this.setState(newState); this.setState(newState);
} }
$('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}}); $('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover click', html: true, delay: {show: 500, hide: 500}});
}, },
onSocketChange: function(msg) { onSocketChange: function(msg) {
if (msg.action === 'new_user') { if (msg.action === 'new_user') {

Просмотреть файл

@@ -32,7 +32,7 @@ module.exports = React.createClass({
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 className="modal-title" id="myModalLabel">{channel.display_name}</h4> <h4 className="modal-title" id="myModalLabel"><span className="name">{channel.display_name}</span></h4>
</div> </div>
<div className="modal-body"> <div className="modal-body">
<div className="row form-group"> <div className="row form-group">

Просмотреть файл

@@ -138,7 +138,7 @@ export default class ChannelInviteModal extends React.Component {
<div className='modal-content'> <div className='modal-content'>
<div className='modal-header'> <div className='modal-header'>
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button> <button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button>
<h4 className='modal-title'>Add New Members to {this.state.channelName}</h4> <h4 className='modal-title'>Add New Members to <span className='name'>{this.state.channel_name}</span></h4>
</div> </div>
<div className='modal-body'> <div className='modal-body'>
{inviteError} {inviteError}

Просмотреть файл

@@ -126,7 +126,7 @@ module.exports = React.createClass({
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">{this.state.channel_name + " Members"}</h4> <h4 className="modal-title"><span className="name">{this.state.channel_name}</span> Members</h4>
<a className="btn btn-md btn-primary" data-toggle="modal" data-target="#channel_invite"><i className="glyphicon glyphicon-envelope"/> Add New Members</a> <a className="btn btn-md btn-primary" data-toggle="modal" data-target="#channel_invite"><i className="glyphicon glyphicon-envelope"/> Add New Members</a>
</div> </div>
<div ref="modalBody" className="modal-body"> <div ref="modalBody" className="modal-body">

Просмотреть файл

@@ -9,159 +9,230 @@ var client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
module.exports = React.createClass({ export default class ChannelNotifications extends React.Component {
componentDidMount: function() { constructor(props) {
ChannelStore.addChangeListener(this._onChange); super(props);
this.onListenerChange = this.onListenerChange.bind(this);
this.updateSection = this.updateSection.bind(this);
this.handleUpdate = this.handleUpdate.bind(this);
this.handleRadioClick = this.handleRadioClick.bind(this);
this.handleQuietToggle = this.handleQuietToggle.bind(this);
this.state = {notifyLevel: '', title: '', channelId: '', activeSection: ''};
}
componentDidMount() {
ChannelStore.addChangeListener(this.onListenerChange);
var self = this; var self = this;
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { $(this.refs.modal.getDOMNode()).on('show.bs.modal', function showModal(e) {
var button = e.relatedTarget; var button = e.relatedTarget;
var channel_id = button.getAttribute('data-channelid'); var channelId = button.getAttribute('data-channelid');
var notifyLevel = ChannelStore.getMember(channel_id).notify_level; var notifyLevel = ChannelStore.getMember(channelId).notify_level;
var quietMode = false; var quietMode = false;
if (notifyLevel === "quiet") quietMode = true;
self.setState({ notify_level: notifyLevel, quiet_mode: quietMode, title: button.getAttribute('data-title'), channel_id: channel_id }); if (notifyLevel === 'quiet') {
quietMode = true;
}
self.setState({notifyLevel: notifyLevel, quietMode: quietMode, title: button.getAttribute('data-title'), channelId: channelId});
}); });
}, }
componentWillUnmount: function() { componentWillUnmount() {
ChannelStore.removeChangeListener(this._onChange); ChannelStore.removeChangeListener(this.onListenerChange);
}, }
_onChange: function() { onListenerChange() {
if (!this.state.channel_id) return; if (!this.state.channelId) {
var notifyLevel = ChannelStore.getMember(this.state.channel_id).notify_level; return;
}
var notifyLevel = ChannelStore.getMember(this.state.channelId).notify_level;
var quietMode = false; var quietMode = false;
if (notifyLevel === "quiet") quietMode = true; if (notifyLevel === 'quiet') {
quietMode = true;
}
var newState = this.state; var newState = this.state;
newState.notify_level = notifyLevel; newState.notifyLevel = notifyLevel;
newState.quiet_mode = quietMode; newState.quietMode = quietMode;
if (!utils.areStatesEqual(this.state, newState)) { if (!utils.areStatesEqual(this.state, newState)) {
this.setState(newState); this.setState(newState);
} }
}, }
updateSection: function(section) { updateSection(section) {
this.setState({ activeSection: section }); this.setState({activeSection: section});
}, }
getInitialState: function() { handleUpdate() {
return { notify_level: "", title: "", channel_id: "", activeSection: "" }; var channelId = this.state.channelId;
}, var notifyLevel = this.state.notifyLevel;
handleUpdate: function() { if (this.state.quietMode) {
var channel_id = this.state.channel_id; notifyLevel = 'quiet';
var notify_level = this.state.quiet_mode ? "quiet" : this.state.notify_level; }
var data = {}; var data = {};
data["channel_id"] = channel_id; data.channel_id = channelId;
data["user_id"] = UserStore.getCurrentId(); data.user_id = UserStore.getCurrentId();
data["notify_level"] = notify_level; data.notify_level = notifyLevel;
if (!data["notify_level"] || data["notify_level"].length === 0) return; if (!data.notify_level || data.notify_level.length === 0) {
return;
}
client.updateNotifyLevel(data, client.updateNotifyLevel(data,
function(data) { function success() {
var member = ChannelStore.getMember(channel_id); var member = ChannelStore.getMember(channelId);
member.notify_level = notify_level; member.notify_level = notifyLevel;
ChannelStore.setChannelMember(member); ChannelStore.setChannelMember(member);
this.updateSection(""); this.updateSection('');
}.bind(this), }.bind(this),
function(err) { function error(err) {
this.setState({ server_error: err.message }); this.setState({serverError: err.message});
}.bind(this) }.bind(this)
); );
}, }
handleRadioClick: function(notifyLevel) { handleRadioClick(notifyLevel) {
this.setState({ notify_level: notifyLevel, quiet_mode: false }); this.setState({notifyLevel: notifyLevel, quietMode: false});
this.refs.modal.getDOMNode().focus(); this.refs.modal.getDOMNode().focus();
}, }
handleQuietToggle: function(quietMode) { handleQuietToggle(quietMode) {
this.setState({ notify_level: "none", quiet_mode: quietMode }); this.setState({notifyLevel: 'none', quietMode: quietMode});
this.refs.modal.getDOMNode().focus(); this.refs.modal.getDOMNode().focus();
}, }
render: function() { render() {
var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null; var serverError = null;
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
}
var self = this; var self = this;
var describe = '';
var inputs = [];
var handleUpdateSection;
var desktopSection; var desktopSection;
if (this.state.activeSection === 'desktop') { if (this.state.activeSection === 'desktop') {
var notifyActive = [false, false, false]; var notifyActive = [false, false, false];
if (this.state.notify_level === "mention") { if (this.state.notifyLevel === 'mention') {
notifyActive[1] = true; notifyActive[1] = true;
} else if (this.state.notify_level === "all") { } else if (this.state.notifyLevel === 'all') {
notifyActive[0] = true; notifyActive[0] = true;
} else { } else {
notifyActive[2] = true; notifyActive[2] = true;
} }
var inputs = [];
inputs.push( inputs.push(
<div> <div>
<div className="radio"> <div className='radio'>
<label> <label>
<input type="radio" checked={notifyActive[0]} onClick={function(){self.handleRadioClick("all")}}>For all activity</input> <input
type='radio'
checked={notifyActive[0]}
onChange={self.handleRadioClick.bind(this, 'all')}
>
For all activity
</input>
</label> </label>
<br/> <br/>
</div> </div>
<div className="radio"> <div className='radio'>
<label> <label>
<input type="radio" checked={notifyActive[1]} onClick={function(){self.handleRadioClick("mention")}}>Only for mentions</input> <input
type='radio'
checked={notifyActive[1]}
onChange={self.handleRadioClick.bind(this, 'mention')}
>
Only for mentions
</input>
</label> </label>
<br/> <br/>
</div> </div>
<div className="radio"> <div className='radio'>
<label> <label>
<input type="radio" checked={notifyActive[2]} onClick={function(){self.handleRadioClick("none")}}>Never</input> <input
type='radio'
checked={notifyActive[2]}
onChange={self.handleRadioClick.bind(this, 'none')}
>
Never
</input>
</label> </label>
</div> </div>
</div> </div>
); );
handleUpdateSection = function updateSection(e) {
self.updateSection('');
self.onListenerChange();
e.preventDefault();
};
desktopSection = ( desktopSection = (
<SettingItemMax <SettingItemMax
title="Send desktop notifications" title='Send desktop notifications'
inputs={inputs} inputs={inputs}
submit={this.handleUpdate} submit={this.handleUpdate}
server_error={server_error} server_error={serverError}
updateSection={function(e){self.updateSection("");self._onChange();e.preventDefault();}} updateSection={handleUpdateSection}
/> />
); );
} else { } else {
var describe = ""; if (this.state.notifyLevel === 'mention') {
if (this.state.notify_level === "mention") { describe = 'Only for mentions';
describe = "Only for mentions"; } else if (this.state.notifyLevel === 'all') {
} else if (this.state.notify_level === "all") { describe = 'For all activity';
describe = "For all activity";
} else { } else {
describe = "Never"; describe = 'Never';
} }
handleUpdateSection = function updateSection(e) {
self.updateSection('desktop');
e.preventDefault();
};
desktopSection = ( desktopSection = (
<SettingItemMin <SettingItemMin
title="Send desktop notifications" title='Send desktop notifications'
describe={describe} describe={describe}
updateSection={function(e){self.updateSection("desktop");e.preventDefault();}} updateSection={handleUpdateSection}
/> />
); );
} }
var quietSection; var quietSection;
if (this.state.activeSection === 'quiet') { if (this.state.activeSection === 'quiet') {
var quietActive = ["",""]; var quietActive = [false, false];
if (this.state.quiet_mode) { if (this.state.quietMode) {
quietActive[0] = "active"; quietActive[0] = true;
} else { } else {
quietActive[1] = "active"; quietActive[1] = true;
} }
var inputs = [];
inputs.push( inputs.push(
<div> <div>
<div className="btn-group" data-toggle="buttons-radio"> <div className='radio'>
<button className={"btn btn-default "+quietActive[0]} onClick={function(){self.handleQuietToggle(true)}}>On</button> <label>
<button className={"btn btn-default "+quietActive[1]} onClick={function(){self.handleQuietToggle(false)}}>Off</button> <input
type='radio'
checked={quietActive[0]}
onChange={self.handleQuietToggle.bind(this, true)}
>
On
</input>
</label>
<br/>
</div>
<div className='radio'>
<label>
<input
type='radio'
checked={quietActive[1]}
onChange={self.handleQuietToggle.bind(this, false)}
>
Off
</input>
</label>
<br/>
</div> </div>
</div> </div>
); );
@@ -173,62 +244,85 @@ module.exports = React.createClass({
</div> </div>
); );
handleUpdateSection = function updateSection(e) {
self.updateSection('');
self.onListenerChange();
e.preventDefault();
};
quietSection = ( quietSection = (
<SettingItemMax <SettingItemMax
title="Quiet mode" title='Quiet mode'
inputs={inputs} inputs={inputs}
submit={this.handleUpdate} submit={this.handleUpdate}
server_error={server_error} server_error={serverError}
updateSection={function(e){self.updateSection("");self._onChange();e.preventDefault();}} updateSection={handleUpdateSection}
/> />
); );
} else { } else {
var describe = ""; if (this.state.quietMode) {
if (this.state.quiet_mode) { describe = 'On';
describe = "On";
} else { } else {
describe = "Off"; describe = 'Off';
} }
handleUpdateSection = function updateSection(e) {
self.updateSection('quiet');
e.preventDefault();
};
quietSection = ( quietSection = (
<SettingItemMin <SettingItemMin
title="Quiet mode" title='Quiet mode'
describe={describe} describe={describe}
updateSection={function(e){self.updateSection("quiet");e.preventDefault();}} updateSection={handleUpdateSection}
/> />
); );
} }
var self = this;
return ( return (
<div className="modal fade" id="channel_notifications" ref="modal" tabIndex="-1" role="dialog" aria-hidden="true"> <div
<div className="modal-dialog settings-modal"> className='modal fade'
<div className="modal-content"> id='channel_notifications'
<div className="modal-header"> ref='modal'
<button type="button" className="close" data-dismiss="modal"> tabIndex='-1'
<span aria-hidden="true">&times;</span> role='dialog'
<span className="sr-only">Close</span> 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'
>
<span aria-hidden='true'>&times;</span>
<span className='sr-only'>Close</span>
</button> </button>
<h4 className="modal-title">{"Notification Preferences for " + this.state.title}</h4> <h4 className='modal-title'>Notification Preferences for <span className='name'>{this.state.title}</span></h4>
</div> </div>
<div className="modal-body"> <div className='modal-body'>
<div className="settings-table"> <div className='settings-table'>
<div className="settings-content"> <div className='settings-content'>
<div ref="wrapper" className="user-settings"> <div
ref='wrapper'
className='user-settings'
>
<br/> <br/>
<div className="divider-dark first"/> <div className='divider-dark first'/>
{desktopSection} {desktopSection}
<div className="divider-light"/> <div className='divider-light'/>
{quietSection} {quietSection}
<div className="divider-dark"/> <div className='divider-dark'/>
</div> </div>
</div> </div>
</div> </div>
{ server_error } {serverError}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); );
} }
}); }

Просмотреть файл

@@ -56,7 +56,7 @@ module.exports = React.createClass({
<div className="modal-content"> <div className="modal-content">
<div className="modal-header"> <div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 className="modal-title" ref="title">Edit {this.state.title} Description</h4> <h4 className="modal-title" ref="title">Edit Description for {this.state.title}</h4>
</div> </div>
<div className="modal-body"> <div className="modal-body">
<textarea className="form-control no-resize" rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea> <textarea className="form-control no-resize" rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea>

Просмотреть файл

@@ -47,7 +47,7 @@ module.exports = React.createClass({
return ( return (
<div> <div>
<h4>{"Find Your " + utils.toTitleCase(strings.Team)}</h4> <h4>{"Find Your " + utils.toTitleCase(strings.Team)}</h4>
<p>{"An email was sent with links to any " + strings.TeamPlural}</p> <p>{"An email was sent with links to any " + strings.TeamPlural + " to which you are a member."}</p>
</div> </div>
); );
} }
@@ -56,7 +56,7 @@ module.exports = React.createClass({
<div> <div>
<h4>Find Your Team</h4> <h4>Find Your Team</h4>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<p>{"We'll send you an email with links to your " + strings.TeamPlural + "."}</p> <p>{"Get an email with links to any " + strings.TeamPlural + " to which you are a member."}</p>
<div className="form-group"> <div className="form-group">
<label className='control-label'>Email</label> <label className='control-label'>Email</label>
<div className={ email_error ? "form-group has-error" : "form-group" }> <div className={ email_error ? "form-group has-error" : "form-group" }>

Просмотреть файл

@@ -14,11 +14,21 @@ function getStateFromStores() {
}; };
} }
module.exports = React.createClass({ export default class MoreChannels extends React.Component {
displayName: 'MoreChannelsModal', constructor(props) {
super(props);
componentDidMount: function() { this.onListenerChange = this.onListenerChange.bind(this);
ChannelStore.addMoreChangeListener(this._onChange); this.handleJoin = this.handleJoin.bind(this);
this.handleNewChannel = this.handleNewChannel.bind(this);
var initState = getStateFromStores();
initState.channelType = '';
initState.joiningChannel = -1;
this.state = initState;
}
componentDidMount() {
ChannelStore.addMoreChangeListener(this.onListenerChange);
$(this.refs.modal.getDOMNode()).on('shown.bs.modal', function shown() { $(this.refs.modal.getDOMNode()).on('shown.bs.modal', function shown() {
asyncClient.getMoreChannels(true); asyncClient.getMoreChannels(true);
}); });
@@ -28,43 +38,42 @@ module.exports = React.createClass({
var button = e.relatedTarget; var button = e.relatedTarget;
self.setState({channelType: $(button).attr('data-channeltype')}); self.setState({channelType: $(button).attr('data-channeltype')});
}); });
}, }
componentWillUnmount: function() { componentWillUnmount() {
ChannelStore.removeMoreChangeListener(this._onChange); ChannelStore.removeMoreChangeListener(this.onListenerChange);
}, }
_onChange: function() { onListenerChange() {
var newState = getStateFromStores(); var newState = getStateFromStores();
if (!utils.areStatesEqual(newState.channels, this.state.channels)) { if (!utils.areStatesEqual(newState.channels, this.state.channels)) {
this.setState(newState); this.setState(newState);
} }
}, }
getInitialState: function() { handleJoin(channel, channelIndex) {
var initState = getStateFromStores(); this.setState({joiningChannel: channelIndex});
initState.channelType = ''; client.joinChannel(channel.id,
return initState; function joinSuccess() {
},
handleJoin: function(id) {
client.joinChannel(id,
function() {
$(this.refs.modal.getDOMNode()).modal('hide'); $(this.refs.modal.getDOMNode()).modal('hide');
asyncClient.getChannel(id); asyncClient.getChannel(channel.id);
utils.switchChannel(channel);
this.setState({joiningChannel: -1});
}.bind(this), }.bind(this),
function(err) { function joinFail(err) {
this.setState({joiningChannel: -1});
this.state.serverError = err.message; this.state.serverError = err.message;
this.setState(this.state); this.setState(this.state);
}.bind(this) }.bind(this)
); );
}, }
handleNewChannel: function() { handleNewChannel() {
$(this.refs.modal.getDOMNode()).modal('hide'); $(this.refs.modal.getDOMNode()).modal('hide');
}, }
render: function() { render() {
var serverError; var serverError;
if (this.state.serverError) { if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>; serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
} }
var outter = this; var self = this;
var moreChannels; var moreChannels;
if (this.state.channels != null) { if (this.state.channels != null) {
@@ -74,14 +83,29 @@ module.exports = React.createClass({
moreChannels = ( moreChannels = (
<table className='more-channel-table table'> <table className='more-channel-table table'>
<tbody> <tbody>
{channels.map(function cMap(channel) { {channels.map(function cMap(channel, index) {
var joinButton;
if (self.state.joiningChannel === index) {
joinButton = (<img
className='join-channel-loading-gif'
src='/static/images/load.gif'
/>);
} else {
joinButton = (<button
onClick={self.handleJoin.bind(self, channel, index)}
className='btn btn-primary'>Join
</button>);
}
return ( return (
<tr key={channel.id}> <tr key={channel.id}>
<td> <td>
<p className='more-channel-name'>{channel.display_name}</p> <p className='more-channel-name'>{channel.display_name}</p>
<p className='more-channel-description'>{channel.description}</p> <p className='more-channel-description'>{channel.description}</p>
</td> </td>
<td className='td--action'><button onClick={outter.handleJoin.bind(outter, channel.id)} className='btn btn-primary'>Join</button></td> <td className='td--action'>
{joinButton}
</td>
</tr> </tr>
); );
})} })}
@@ -102,23 +126,47 @@ module.exports = React.createClass({
} }
return ( return (
<div className='modal fade' id='more_channels' ref='modal' tabIndex='-1' role='dialog' aria-hidden='true'> <div
className='modal fade'
id='more_channels'
ref='modal'
tabIndex='-1'
role='dialog'
aria-hidden='true'
>
<div className='modal-dialog'> <div className='modal-dialog'>
<div className='modal-content'> <div className='modal-content'>
<div className='modal-header'> <div className='modal-header'>
<button type='button' className='close' data-dismiss='modal'> <button
type='button'
className='close'
data-dismiss='modal'
>
<span aria-hidden='true'>&times;</span> <span aria-hidden='true'>&times;</span>
<span className='sr-only'>Close</span> <span className='sr-only'>Close</span>
</button> </button>
<h4 className='modal-title'>More Channels</h4> <h4 className='modal-title'>More Channels</h4>
<button data-toggle='modal' data-target='#new_channel' data-channeltype={this.state.channelType} type='button' className='btn btn-primary channel-create-btn' onClick={this.handleNewChannel}>Create New Channel</button> <button
data-toggle='modal'
data-target='#new_channel'
data-channeltype={this.state.channelType}
type='button'
className='btn btn-primary channel-create-btn'
onClick={this.handleNewChannel}>Create New Channel
</button>
</div> </div>
<div className='modal-body'> <div className='modal-body'>
{moreChannels} {moreChannels}
{serverError} {serverError}
</div> </div>
<div className='modal-footer'> <div className='modal-footer'>
<button type='button' className='btn btn-default' data-dismiss='modal'>Close</button> <button
type='button'
className='btn btn-default'
data-dismiss='modal'
>
Close
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -126,4 +174,4 @@ module.exports = React.createClass({
); );
} }
}); }

Просмотреть файл

@@ -152,6 +152,8 @@ module.exports = React.createClass({
var channelMenuDropdown = null; var channelMenuDropdown = null;
if (channel) { if (channel) {
var viewInfoOption = <li role='presentation'><a role='menuitem' data-toggle='modal' data-target='#channel_info' data-channelid={channel.id} href='#'>View Info</a></li>
var addMembersOption = null; var addMembersOption = null;
if (!isDirect && !ChannelStore.isDefault(channel)) { if (!isDirect && !ChannelStore.isDefault(channel)) {
addMembersOption = <li role='presentation'><a role='menuitem' data-toggle='modal' data-target='#channel_invite' href='#'>Add Members</a></li>; addMembersOption = <li role='presentation'><a role='menuitem' data-toggle='modal' data-target='#channel_invite' href='#'>Add Members</a></li>;
@@ -192,6 +194,7 @@ module.exports = React.createClass({
<span className='glyphicon glyphicon-chevron-down header-dropdown__icon'></span> <span className='glyphicon glyphicon-chevron-down header-dropdown__icon'></span>
</a> </a>
<ul className='dropdown-menu' role='menu' aria-labelledby='channel_header_dropdown'> <ul className='dropdown-menu' role='menu' aria-labelledby='channel_header_dropdown'>
{viewInfoOption}
{addMembersOption} {addMembersOption}
{manageMembersOption} {manageMembersOption}
{setChannelDescriptionOption} {setChannelDescriptionOption}

Просмотреть файл

@@ -75,6 +75,7 @@ module.exports = React.createClass({
utils.changeCss('.mention-link', 'color: ' + user.props.theme + ';'); utils.changeCss('.mention-link', 'color: ' + user.props.theme + ';');
utils.changeCss('@media(max-width: 768px){.search-bar__container', 'background: ' + user.props.theme + ';}'); utils.changeCss('@media(max-width: 768px){.search-bar__container', 'background: ' + user.props.theme + ';}');
utils.changeCss('.search-item-container:hover', 'background: ' + utils.changeOpacity(user.props.theme, 0.05) + ';'); utils.changeCss('.search-item-container:hover', 'background: ' + utils.changeOpacity(user.props.theme, 0.05) + ';');
utils.changeCss('.nav-pills__unread-indicator', 'background: ' + utils.changeOpacity(user.props.theme, 0.05) + ';');
} }
if (user.props.theme !== '#000000' && user.props.theme !== '#585858') { if (user.props.theme !== '#000000' && user.props.theme !== '#585858') {
@@ -377,8 +378,8 @@ module.exports = React.createClass({
<strong><UserProfile userId={teammate.id} /></strong> <strong><UserProfile userId={teammate.id} /></strong>
</div> </div>
<p className='channel-intro-text'> <p className='channel-intro-text'>
{'This is the start of your private message history with ' + teammateName + '.'}<br/> This is the start of your private message history with <strong>{teammateName}</strong>.<br/>
{'Private messages and files shared here are not shown to people outside this area.'} Private messages and files shared here are not shown to people outside this area.
</p> </p>
<a className='intro-links' href='#' style={userStyle} data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}><i className='fa fa-pencil'></i>Set a description</a> <a className='intro-links' href='#' style={userStyle} data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}><i className='fa fa-pencil'></i>Set a description</a>
</div> </div>
@@ -416,9 +417,9 @@ module.exports = React.createClass({
<div className='channel-intro'> <div className='channel-intro'>
<h4 className='channel-intro__title'>Beginning of {uiName}</h4> <h4 className='channel-intro__title'>Beginning of {uiName}</h4>
<p className='channel-intro__content'> <p className='channel-intro__content'>
Welcome to {uiName}! Welcome to <strong>{uiName}</strong>!
<br/><br/> <br/><br/>
{'This is the first channel ' + strings.Team + 'mates see when they'} This is the first channel {strings.Team}mates see when they
<br/> <br/>
sign up - use it for posting updates everyone needs to know. sign up - use it for posting updates everyone needs to know.
<br/><br/> <br/><br/>
@@ -434,7 +435,7 @@ module.exports = React.createClass({
<div className='channel-intro'> <div className='channel-intro'>
<h4 className='channel-intro__title'>Beginning of {uiName}</h4> <h4 className='channel-intro__title'>Beginning of {uiName}</h4>
<p className='channel-intro__content'> <p className='channel-intro__content'>
{'This is the start of ' + uiName + ', a channel for non-work-related conversations.'} This is the start of <strong>{uiName}</strong>, a channel for non-work-related conversations.
<br/> <br/>
</p> </p>
<a className='intro-links' href='#' style={userStyle} data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={uiName} data-channelid={channel.id}><i className='fa fa-pencil'></i>Set a description</a> <a className='intro-links' href='#' style={userStyle} data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={uiName} data-channelid={channel.id}><i className='fa fa-pencil'></i>Set a description</a>
@@ -453,7 +454,7 @@ module.exports = React.createClass({
var createMessage; var createMessage;
if (creatorName !== '') { if (creatorName !== '') {
createMessage = 'This is the start of the ' + uiName + ' ' + uiType + ', created by ' + creatorName + ' on ' + utils.displayDate(channel.create_at) + '.'; createMessage = (<span>This is the start of the <strong>{uiName}</strong> {uiType}, created by <strong>{creatorName}</strong> on <strong>{utils.displayDate(channel.create_at)}</strong></span>);
} else { } else {
createMessage = 'This is the start of the ' + uiName + ' ' + uiType + ', created on ' + utils.displayDate(channel.create_at) + '.'; createMessage = 'This is the start of the ' + uiName + ' ' + uiType + ', created on ' + utils.displayDate(channel.create_at) + '.';
} }

Просмотреть файл

@@ -20,7 +20,7 @@ module.exports = React.createClass({
var townSquare = ChannelStore.getByName("town-square"); var townSquare = ChannelStore.getByName("town-square");
utils.switchChannel(townSquare); utils.switchChannel(townSquare);
this.setState({channelName: "", remover: ""}) this.setState({channelName: "", remover: ""});
}, },
componentDidMount: function() { componentDidMount: function() {
$(this.getDOMNode()).on('show.bs.modal',this.handleShow); $(this.getDOMNode()).on('show.bs.modal',this.handleShow);
@@ -40,18 +40,18 @@ module.exports = React.createClass({
if (currentUser != null) { if (currentUser != null) {
return ( return (
<div className="modal fade" ref="modal" id="removed_from_channel" tabIndex="-1" role="dialog" aria-hidden="true"> <div className='modal fade' ref='modal' id='removed_from_channel' tabIndex='-1' role='dialog' aria-hidden='true'>
<div className="modal-dialog"> <div className='modal-dialog'>
<div className="modal-content"> <div className='modal-content'>
<div className="modal-header"> <div className='modal-header'>
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button>
<h4 className="modal-title">Removed from {channelName}</h4> <h4 className='modal-title'>Removed from <span className='name'>{channelName}</span></h4>
</div> </div>
<div className="modal-body"> <div className='modal-body'>
<p>{remover} removed you from {channelName}</p> <p>{remover} removed you from {channelName}</p>
</div> </div>
<div className="modal-footer"> <div className='modal-footer'>
<button type="button" className="btn btn-primary" data-dismiss="modal">Okay</button> <button type='button' className='btn btn-primary' data-dismiss='modal'>Okay</button>
</div> </div>
</div> </div>
</div> </div>

Просмотреть файл

@@ -20,7 +20,7 @@ module.exports = React.createClass({
<hr /> <hr />
{ server_error } { server_error }
{ clientError } { clientError }
{ this.props.submit ? <a className="btn btn-sm btn-primary" onClick={this.props.submit}>Submit</a> : "" } { this.props.submit ? <a className="btn btn-sm btn-primary" href="#" onClick={this.props.submit}>Submit</a> : "" }
<a className="btn btn-sm theme" href="#" onClick={this.props.updateSection}>Cancel</a> <a className="btn btn-sm theme" href="#" onClick={this.props.updateSection}>Cancel</a>
</li> </li>
</ul> </ul>

Просмотреть файл

@@ -67,7 +67,7 @@ module.exports = React.createClass({
<li className='setting-list-item'> <li className='setting-list-item'>
{serverError} {serverError}
{clientError} {clientError}
<span className='btn btn-sm btn-primary btn-file sel-btn'>SelectFile<input ref='uploadinput' accept={this.props.fileTypesAccepted} type='file' onChange={this.onFileSelect}/></span> <span className='btn btn-sm btn-primary btn-file sel-btn'>Select File<input ref='uploadinput' accept={this.props.fileTypesAccepted} type='file' onChange={this.onFileSelect}/></span>
<a className={'btn btn-sm btn-primary'} onClick={this.doSubmit}>Import</a> <a className={'btn btn-sm btn-primary'} onClick={this.doSubmit}>Import</a>
<a className='btn btn-sm theme' href='#' onClick={this.doCancel}>Cancel</a> <a className='btn btn-sm theme' href='#' onClick={this.doCancel}>Cancel</a>
</li> </li>

Просмотреть файл

@@ -39,12 +39,12 @@ module.exports = React.createClass({
break; break;
case 'done': case 'done':
messageSection = ( messageSection = (
<p>Import sucessfull: <a href={this.state.link} download='MattermostImportSummery.txt'>View Summery</a></p> <p>Import sucessfull: <a href={this.state.link} download='MattermostImportSummary.txt'>View Summary</a></p>
); );
break; break;
case 'fail': case 'fail':
messageSection = ( messageSection = (
<p>Import failure: <a href={this.state.link} download='MattermostImportSummery.txt'>View Summery</a></p> <p>Import failure: <a href={this.state.link} download='MattermostImportSummary.txt'>View Summary</a></p>
); );
break; break;
} }

Просмотреть файл

@@ -67,16 +67,28 @@ function getNotificationsStateFromStores() {
return {notifyLevel: desktop, enableEmail: email, soundNeeded: soundNeeded, enableSound: sound, usernameKey: usernameKey, mentionKey: mentionKey, customKeys: customKeys, customKeysChecked: customKeys.length > 0, firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey}; return {notifyLevel: desktop, enableEmail: email, soundNeeded: soundNeeded, enableSound: sound, usernameKey: usernameKey, mentionKey: mentionKey, customKeys: customKeys, customKeysChecked: customKeys.length > 0, firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey};
} }
module.exports = React.createClass({ export default class NotificationsTab extends React.Component {
displayName: 'NotificationsTab', constructor(props) {
propTypes: { super(props);
user: React.PropTypes.object,
updateSection: React.PropTypes.func, this.handleSubmit = this.handleSubmit.bind(this);
updateTab: React.PropTypes.func, this.handleClose = this.handleClose.bind(this);
activeSection: React.PropTypes.string, this.updateSection = this.updateSection.bind(this);
activeTab: React.PropTypes.string this.onListenerChange = this.onListenerChange.bind(this);
}, this.handleNotifyRadio = this.handleNotifyRadio.bind(this);
handleSubmit: function() { this.handleEmailRadio = this.handleEmailRadio.bind(this);
this.handleSoundRadio = this.handleSoundRadio.bind(this);
this.updateUsernameKey = this.updateUsernameKey.bind(this);
this.updateMentionKey = this.updateMentionKey.bind(this);
this.updateFirstNameKey = this.updateFirstNameKey.bind(this);
this.updateAllKey = this.updateAllKey.bind(this);
this.updateChannelKey = this.updateChannelKey.bind(this);
this.updateCustomMentionKeys = this.updateCustomMentionKeys.bind(this);
this.onCustomChange = this.onCustomChange.bind(this);
this.state = getNotificationsStateFromStores();
}
handleSubmit() {
var data = {}; var data = {};
data.user_id = this.props.user.id; data.user_id = this.props.user.id;
data.email = this.state.enableEmail; data.email = this.state.enableEmail;
@@ -110,66 +122,63 @@ module.exports = React.createClass({
this.setState({serverError: err.message}); this.setState({serverError: err.message});
}.bind(this) }.bind(this)
); );
}, }
handleClose: function() { handleClose() {
$(this.getDOMNode()).find('.form-control').each(function clearField() { $(React.findDOMNode(this)).find('.form-control').each(function clearField() {
this.value = ''; this.value = '';
}); });
this.setState(assign({}, getNotificationsStateFromStores(), {serverError: null})); this.setState(assign({}, getNotificationsStateFromStores(), {serverError: null}));
this.props.updateTab('general'); this.props.updateTab('general');
}, }
updateSection: function(section) { updateSection(section) {
this.setState(this.getInitialState()); this.setState(this.getInitialState());
this.props.updateSection(section); this.props.updateSection(section);
}, }
componentDidMount: function() { componentDidMount() {
UserStore.addChangeListener(this.onListenerChange); UserStore.addChangeListener(this.onListenerChange);
$('#user_settings').on('hidden.bs.modal', this.handleClose); $('#user_settings').on('hidden.bs.modal', this.handleClose);
}, }
componentWillUnmount: function() { componentWillUnmount() {
UserStore.removeChangeListener(this.onListenerChange); UserStore.removeChangeListener(this.onListenerChange);
$('#user_settings').off('hidden.bs.modal', this.handleClose); $('#user_settings').off('hidden.bs.modal', this.handleClose);
this.props.updateSection(''); this.props.updateSection('');
}, }
onListenerChange: function() { onListenerChange() {
var newState = getNotificationsStateFromStores(); var newState = getNotificationsStateFromStores();
if (!utils.areStatesEqual(newState, this.state)) { if (!utils.areStatesEqual(newState, this.state)) {
this.setState(newState); this.setState(newState);
} }
}, }
getInitialState: function() { handleNotifyRadio(notifyLevel) {
return getNotificationsStateFromStores();
},
handleNotifyRadio: function(notifyLevel) {
this.setState({notifyLevel: notifyLevel}); this.setState({notifyLevel: notifyLevel});
this.refs.wrapper.getDOMNode().focus(); this.refs.wrapper.getDOMNode().focus();
}, }
handleEmailRadio: function(enableEmail) { handleEmailRadio(enableEmail) {
this.setState({enableEmail: enableEmail}); this.setState({enableEmail: enableEmail});
this.refs.wrapper.getDOMNode().focus(); this.refs.wrapper.getDOMNode().focus();
}, }
handleSoundRadio: function(enableSound) { handleSoundRadio(enableSound) {
this.setState({enableSound: enableSound}); this.setState({enableSound: enableSound});
this.refs.wrapper.getDOMNode().focus(); this.refs.wrapper.getDOMNode().focus();
}, }
updateUsernameKey: function(val) { updateUsernameKey(val) {
this.setState({usernameKey: val}); this.setState({usernameKey: val});
}, }
updateMentionKey: function(val) { updateMentionKey(val) {
this.setState({mentionKey: val}); this.setState({mentionKey: val});
}, }
updateFirstNameKey: function(val) { updateFirstNameKey(val) {
this.setState({firstNameKey: val}); this.setState({firstNameKey: val});
}, }
updateAllKey: function(val) { updateAllKey(val) {
this.setState({allKey: val}); this.setState({allKey: val});
}, }
updateChannelKey: function(val) { updateChannelKey(val) {
this.setState({channelKey: val}); this.setState({channelKey: val});
}, }
updateCustomMentionKeys: function() { updateCustomMentionKeys() {
var checked = this.refs.customcheck.getDOMNode().checked; var checked = this.refs.customcheck.getDOMNode().checked;
if (checked) { if (checked) {
@@ -180,12 +189,12 @@ module.exports = React.createClass({
} else { } else {
this.setState({customKeys: '', customKeysChecked: false}); this.setState({customKeys: '', customKeysChecked: false});
} }
}, }
onCustomChange: function() { onCustomChange() {
this.refs.customcheck.getDOMNode().checked = true; this.refs.customcheck.getDOMNode().checked = true;
this.updateCustomMentionKeys(); this.updateCustomMentionKeys();
}, }
render: function() { render() {
var serverError = null; var serverError = null;
if (this.state.serverError) { if (this.state.serverError) {
serverError = this.state.serverError; serverError = this.state.serverError;
@@ -196,6 +205,7 @@ module.exports = React.createClass({
var user = this.props.user; var user = this.props.user;
var desktopSection; var desktopSection;
var handleUpdateDesktopSection;
if (this.props.activeSection === 'desktop') { if (this.props.activeSection === 'desktop') {
var notifyActive = [false, false, false]; var notifyActive = [false, false, false];
if (this.state.notifyLevel === 'mention') { if (this.state.notifyLevel === 'mention') {
@@ -206,41 +216,63 @@ module.exports = React.createClass({
notifyActive[0] = true; notifyActive[0] = true;
} }
var inputs = []; let inputs = [];
inputs.push( inputs.push(
<div> <div>
<div className='radio'> <div className='radio'>
<label> <label>
<input type='radio' checked={notifyActive[0]} onClick={function(){self.handleNotifyRadio('all')}}>For all activity</input> <input type='radio'
checked={notifyActive[0]}
onChange={self.handleNotifyRadio.bind(this, 'all')}
>
For all activity
</input>
</label> </label>
<br/> <br/>
</div> </div>
<div className='radio'> <div className='radio'>
<label> <label>
<input type='radio' checked={notifyActive[1]} onClick={function(){self.handleNotifyRadio('mention')}}>Only for mentions and private messages</input> <input
type='radio'
checked={notifyActive[1]}
onChange={self.handleNotifyRadio.bind(this, 'mention')}
>
Only for mentions and private messages
</input>
</label> </label>
<br/> <br/>
</div> </div>
<div className='radio'> <div className='radio'>
<label> <label>
<input type='radio' checked={notifyActive[2]} onClick={function(){self.handleNotifyRadio('none')}}>Never</input> <input
type='radio'
checked={notifyActive[2]}
onChange={self.handleNotifyRadio.bind(this, 'none')}
>
Never
</input>
</label> </label>
</div> </div>
</div> </div>
); );
handleUpdateDesktopSection = function updateDesktopSection(e) {
self.props.updateSection('');
e.preventDefault();
};
desktopSection = ( desktopSection = (
<SettingItemMax <SettingItemMax
title='Send desktop notifications' title='Send desktop notifications'
inputs={inputs} inputs={inputs}
submit={this.handleSubmit} submit={this.handleSubmit}
server_error={serverError} server_error={serverError}
updateSection={function(e){self.updateSection('');e.preventDefault();}} updateSection={handleUpdateDesktopSection}
/> />
); );
} else { } else {
var describe = ''; let describe = '';
if (this.state.notifyLevel === 'mention') { if (this.state.notifyLevel === 'mention') {
describe = 'Only for mentions and private messages'; describe = 'Only for mentions and private messages';
} else if (this.state.notifyLevel === 'none') { } else if (this.state.notifyLevel === 'none') {
@@ -249,132 +281,278 @@ module.exports = React.createClass({
describe = 'For all activity'; describe = 'For all activity';
} }
handleUpdateDesktopSection = function updateDesktopSection() {
self.props.updateSection('desktop');
};
desktopSection = ( desktopSection = (
<SettingItemMin <SettingItemMin
title='Send desktop notifications' title='Send desktop notifications'
describe={describe} describe={describe}
updateSection={function(){self.updateSection('desktop');}} updateSection={handleUpdateDesktopSection}
/> />
); );
} }
var soundSection; var soundSection;
var handleUpdateSoundSection;
if (this.props.activeSection === 'sound' && this.state.soundNeeded) { if (this.props.activeSection === 'sound' && this.state.soundNeeded) {
var soundActive = ['', '']; var soundActive = [false, false];
if (this.state.enableSound === 'false') { if (this.state.enableSound === 'false') {
soundActive[1] = 'active'; soundActive[1] = true;
} else { } else {
soundActive[0] = 'active'; soundActive[0] = true;
} }
var inputs = []; let inputs = [];
inputs.push( inputs.push(
<div> <div>
<div className='btn-group' data-toggle='buttons-radio'> <div className='radio'>
<button className={'btn btn-default '+soundActive[0]} onClick={function(){self.handleSoundRadio('true')}}>On</button> <label>
<button className={'btn btn-default '+soundActive[1]} onClick={function(){self.handleSoundRadio('false')}}>Off</button> <input
type='radio'
checked={soundActive[0]}
onChange={self.handleSoundRadio.bind(this, 'true')}
>
On
</input>
</label>
<br/>
</div> </div>
</div> <div className='radio'>
<label>
<input
type='radio'
checked={soundActive[1]}
onChange={self.handleSoundRadio.bind(this, 'false')}
>
Off
</input>
</label>
<br/>
</div>
</div>
); );
handleUpdateSoundSection = function updateSoundSection(e) {
self.props.updateSection('');
e.preventDefault();
};
soundSection = ( soundSection = (
<SettingItemMax <SettingItemMax
title='Desktop notification sounds' title='Desktop notification sounds'
inputs={inputs} inputs={inputs}
submit={this.handleSubmit} submit={this.handleSubmit}
server_error={serverError} server_error={serverError}
updateSection={function(e){self.updateSection('');e.preventDefault();}} updateSection={handleUpdateSoundSection}
/> />
); );
} else { } else {
var describe = ''; let describe = '';
if (!this.state.soundNeeded) { if (!this.state.soundNeeded) {
describe = 'Please configure notification sounds in your browser settings' describe = 'Please configure notification sounds in your browser settings';
} else if (this.state.enableSound === 'false') { } else if (this.state.enableSound === 'false') {
describe = 'Off'; describe = 'Off';
} else { } else {
describe = 'On'; describe = 'On';
} }
handleUpdateSoundSection = function updateSoundSection() {
self.props.updateSection('sound');
};
soundSection = ( soundSection = (
<SettingItemMin <SettingItemMin
title='Desktop notification sounds' title='Desktop notification sounds'
describe={describe} describe={describe}
updateSection={function(){self.updateSection('sound');}} updateSection={handleUpdateSoundSection}
disableOpen = {!this.state.soundNeeded} disableOpen = {!this.state.soundNeeded}
/> />
); );
} }
var emailSection; var emailSection;
var handleUpdateEmailSection;
if (this.props.activeSection === 'email') { if (this.props.activeSection === 'email') {
var emailActive = ['','']; var emailActive = [false, false];
if (this.state.enableEmail === 'false') { if (this.state.enableEmail === 'false') {
emailActive[1] = 'active'; emailActive[1] = true;
} else { } else {
emailActive[0] = 'active'; emailActive[0] = true;
} }
var inputs = []; let inputs = [];
inputs.push( inputs.push(
<div> <div>
<div className='btn-group' data-toggle='buttons-radio'> <div className='radio'>
<button className={'btn btn-default '+emailActive[0]} onClick={function(){self.handleEmailRadio('true')}}>On</button> <label>
<button className={'btn btn-default '+emailActive[1]} onClick={function(){self.handleEmailRadio('false')}}>Off</button> <input
type='radio'
checked={emailActive[0]}
onChange={self.handleEmailRadio.bind(this, 'true')}
>
On
</input>
</label>
<br/>
</div>
<div className='radio'>
<label>
<input
type='radio'
checked={emailActive[1]}
onChange={self.handleEmailRadio.bind(this, 'false')}
>
Off
</input>
</label>
<br/>
</div> </div>
<div><br/>{'Email notifications are sent for mentions and private messages after you have been away from ' + config.SiteName + ' for 5 minutes.'}</div> <div><br/>{'Email notifications are sent for mentions and private messages after you have been away from ' + config.SiteName + ' for 5 minutes.'}</div>
</div> </div>
); );
handleUpdateEmailSection = function updateEmailSection(e) {
self.props.updateSection('');
e.preventDefault();
};
emailSection = ( emailSection = (
<SettingItemMax <SettingItemMax
title='Email notifications' title='Email notifications'
inputs={inputs} inputs={inputs}
submit={this.handleSubmit} submit={this.handleSubmit}
server_error={serverError} server_error={serverError}
updateSection={function(e){self.updateSection('');e.preventDefault();}} updateSection={handleUpdateEmailSection}
/> />
); );
} else { } else {
var describe = ''; let describe = '';
if (this.state.enableEmail === 'false') { if (this.state.enableEmail === 'false') {
describe = 'Off'; describe = 'Off';
} else { } else {
describe = 'On'; describe = 'On';
} }
handleUpdateEmailSection = function updateEmailSection() {
self.props.updateSection('email');
};
emailSection = ( emailSection = (
<SettingItemMin <SettingItemMin
title='Email notifications' title='Email notifications'
describe={describe} describe={describe}
updateSection={function(){self.updateSection('email');}} updateSection={handleUpdateEmailSection}
/> />
); );
} }
var keysSection; var keysSection;
var handleUpdateKeysSection;
if (this.props.activeSection === 'keys') { if (this.props.activeSection === 'keys') {
var inputs = []; let inputs = [];
let handleUpdateFirstNameKey;
let handleUpdateUsernameKey;
let handleUpdateMentionKey;
let handleUpdateAllKey;
let handleUpdateChannelKey;
if (user.first_name) { if (user.first_name) {
handleUpdateFirstNameKey = function handleFirstNameKeyChange(e) {
self.updateFirstNameKey(e.target.checked);
};
inputs.push( inputs.push(
<div> <div>
<div className='checkbox'> <div className='checkbox'>
<label> <label>
<input type='checkbox' checked={this.state.firstNameKey} onChange={function(e){self.updateFirstNameKey(e.target.checked);}}>{'Your case sensitive first name "' + user.first_name + '"'}</input> <input
type='checkbox'
checked={this.state.firstNameKey}
onChange={handleUpdateFirstNameKey}
>
{'Your case sensitive first name "' + user.first_name + '"'}
</input>
</label> </label>
</div> </div>
</div> </div>
); );
} }
handleUpdateUsernameKey = function handleUsernameKeyChange(e) {
self.updateUsernameKey(e.target.checked);
};
inputs.push( inputs.push(
<div> <div>
<div className='checkbox'> <div className='checkbox'>
<label> <label>
<input type='checkbox' checked={this.state.usernameKey} onChange={function(e){self.updateUsernameKey(e.target.checked);}}>{'Your non-case sensitive username "' + user.username + '"'}</input> <input
type='checkbox'
checked={this.state.usernameKey}
onChange={handleUpdateUsernameKey}
>
{'Your non-case sensitive username "' + user.username + '"'}
</input>
</label>
</div>
</div>
);
handleUpdateMentionKey = function handleMentionKeyChange(e) {
self.updateMentionKey(e.target.checked);
};
inputs.push(
<div>
<div className='checkbox'>
<label>
<input
type='checkbox'
checked={this.state.mentionKey}
onChange={handleUpdateMentionKey}
>
{'Your username mentioned "@' + user.username + '"'}
</input>
</label>
</div>
</div>
);
handleUpdateAllKey = function handleAllKeyChange(e) {
self.updateAllKey(e.target.checked);
};
inputs.push(
<div>
<div className='checkbox'>
<label>
<input
type='checkbox'
checked={this.state.allKey}
onChange={handleUpdateAllKey}
>
{'Team-wide mentions "@all"'}
</input>
</label>
</div>
</div>
);
handleUpdateChannelKey = function handleChannelKeyChange(e) {
self.updateChannelKey(e.target.checked);
};
inputs.push(
<div>
<div className='checkbox'>
<label>
<input
type='checkbox'
checked={this.state.channelKey}
onChange={handleUpdateChannelKey}
>
{'Channel-wide mentions "@channel"'}
</input>
</label> </label>
</div> </div>
</div> </div>
@@ -384,62 +562,61 @@ module.exports = React.createClass({
<div> <div>
<div className='checkbox'> <div className='checkbox'>
<label> <label>
<input type='checkbox' checked={this.state.mentionKey} onChange={function(e){self.updateMentionKey(e.target.checked);}}>{'Your username mentioned "@' + user.username + '"'}</input> <input
ref='customcheck'
type='checkbox'
checked={this.state.customKeysChecked}
onChange={this.updateCustomMentionKeys}
>
{'Other non-case sensitive words, separated by commas:'}
</input>
</label> </label>
</div> </div>
<input
ref='custommentions'
className='form-control mentions-input'
type='text'
defaultValue={this.state.customKeys}
onChange={this.onCustomChange}
/>
</div> </div>
); );
inputs.push( handleUpdateKeysSection = function updateKeysSection(e) {
<div> self.props.updateSection('');
<div className='checkbox'> e.preventDefault();
<label> };
<input type='checkbox' checked={this.state.allKey} onChange={function(e){self.updateAllKey(e.target.checked);}}>{'Team-wide mentions "@all"'}</input>
</label>
</div>
</div>
);
inputs.push(
<div>
<div className='checkbox'>
<label>
<input type='checkbox' checked={this.state.channelKey} onChange={function(e){self.updateChannelKey(e.target.checked);}}>{'Channel-wide mentions "@channel"'}</input>
</label>
</div>
</div>
);
inputs.push(
<div>
<div className='checkbox'>
<label>
<input ref='customcheck' type='checkbox' checked={this.state.customKeysChecked} onChange={this.updateCustomMentionKeys}>{'Other non-case sensitive words, separated by commas:'}</input>
</label>
</div>
<input ref='custommentions' className='form-control mentions-input' type='text' defaultValue={this.state.customKeys} onChange={this.onCustomChange} />
</div>
);
keysSection = ( keysSection = (
<SettingItemMax <SettingItemMax
title='Words that trigger mentions' title='Words that trigger mentions'
inputs={inputs} inputs={inputs}
submit={this.handleSubmit} submit={this.handleSubmit}
server_error={serverError} server_error={serverError}
updateSection={function(e){self.updateSection('');e.preventDefault();}} updateSection={handleUpdateKeysSection}
/> />
); );
} else { } else {
var keys = []; let keys = [];
if (this.state.firstNameKey) keys.push(user.first_name); if (this.state.firstNameKey) {
if (this.state.usernameKey) keys.push(user.username); keys.push(user.first_name);
if (this.state.mentionKey) keys.push('@'+user.username); }
if (this.state.allKey) keys.push('@all'); if (this.state.usernameKey) {
if (this.state.channelKey) keys.push('@channel'); keys.push(user.username);
if (this.state.customKeys.length > 0) keys = keys.concat(this.state.customKeys.split(',')); }
if (this.state.mentionKey) {
keys.push('@' + user.username);
}
if (this.state.allKey) {
keys.push('@all');
}
if (this.state.channelKey) {
keys.push('@channel');
}
if (this.state.customKeys.length > 0) {
keys = keys.concat(this.state.customKeys.split(','));
}
var describe = ''; let describe = '';
for (var i = 0; i < keys.length; i++) { for (var i = 0; i < keys.length; i++) {
describe += '"' + keys[i] + '", '; describe += '"' + keys[i] + '", ';
} }
@@ -450,11 +627,15 @@ module.exports = React.createClass({
describe = 'No words configured'; describe = 'No words configured';
} }
handleUpdateKeysSection = function updateKeysSection() {
self.props.updateSection('keys');
};
keysSection = ( keysSection = (
<SettingItemMin <SettingItemMin
title='Words that trigger mentions' title='Words that trigger mentions'
describe={describe} describe={describe}
updateSection={function(){self.updateSection('keys');}} updateSection={handleUpdateKeysSection}
/> />
); );
} }
@@ -462,10 +643,26 @@ module.exports = React.createClass({
return ( return (
<div> <div>
<div className='modal-header'> <div className='modal-header'>
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button> <button
<h4 className='modal-title' ref='title'><i className='modal-back'></i>Notifications</h4> 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>
Notifications
</h4>
</div> </div>
<div ref='wrapper' className='user-settings'> <div
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>Notifications</h3> <h3 className='tab-header'>Notifications</h3>
<div className='divider-dark first'/> <div className='divider-dark first'/>
{desktopSection} {desktopSection}
@@ -481,4 +678,17 @@ module.exports = React.createClass({
); );
} }
}); }
NotificationsTab.defaultProps = {
user: null,
activeSection: '',
activeTab: ''
};
NotificationsTab.propTypes = {
user: React.PropTypes.object,
updateSection: React.PropTypes.func,
updateTab: React.PropTypes.func,
activeSection: React.PropTypes.string,
activeTab: React.PropTypes.string
};

Просмотреть файл

@@ -62,11 +62,9 @@ module.exports = React.createClass({
this.setState({confirmPassword: e.target.value}); this.setState({confirmPassword: e.target.value});
}, },
handleHistoryOpen: function() { handleHistoryOpen: function() {
this.setState({willReturn: true});
$("#user_settings").modal('hide'); $("#user_settings").modal('hide');
}, },
handleDevicesOpen: function() { handleDevicesOpen: function() {
this.setState({willReturn: true});
$("#user_settings").modal('hide'); $("#user_settings").modal('hide');
}, },
handleClose: function() { handleClose: function() {
@@ -75,11 +73,7 @@ module.exports = React.createClass({
}); });
this.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null}); this.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null});
if (!this.state.willReturn) { this.props.updateTab('general');
this.props.updateTab('general');
} else {
this.setState({willReturn: false});
}
}, },
componentDidMount: function() { componentDidMount: function() {
$('#user_settings').on('hidden.bs.modal', this.handleClose); $('#user_settings').on('hidden.bs.modal', this.handleClose);
@@ -89,7 +83,7 @@ module.exports = React.createClass({
this.props.updateSection(''); this.props.updateSection('');
}, },
getInitialState: function() { getInitialState: function() {
return {currentPassword: '', newPassword: '', confirmPassword: '', willReturn: false}; return {currentPassword: '', newPassword: '', confirmPassword: ''};
}, },
render: function() { render: function() {
var serverError = this.state.serverError ? this.state.serverError : null; var serverError = this.state.serverError ? this.state.serverError : null;

Просмотреть файл

@@ -109,6 +109,26 @@ module.exports = React.createClass({
} }
); );
if (this.refs.previewArrowLeft) {
$(this.refs.previewArrowLeft.getDOMNode()).hover(
function onModalHover() {
$(self.refs.imageFooter.getDOMNode()).addClass('footer--show');
}, function offModalHover() {
$(self.refs.imageFooter.getDOMNode()).removeClass('footer--show');
}
);
}
if (this.refs.previewArrowRight) {
$(this.refs.previewArrowRight.getDOMNode()).hover(
function onModalHover() {
$(self.refs.imageFooter.getDOMNode()).addClass('footer--show');
}, function offModalHover() {
$(self.refs.imageFooter.getDOMNode()).removeClass('footer--show');
}
);
}
$(window).on('keyup', this.handleKeyPress); $(window).on('keyup', this.handleKeyPress);
// keep track of whether or not this component is mounted so we can safely set the state asynchronously // keep track of whether or not this component is mounted so we can safely set the state asynchronously
@@ -252,13 +272,21 @@ module.exports = React.createClass({
var rightArrow = ''; var rightArrow = '';
if (this.props.filenames.length > 1) { if (this.props.filenames.length > 1) {
leftArrow = ( leftArrow = (
<a className='modal-prev-bar' href='#' onClick={this.handlePrev}> <a
ref='previewArrowLeft'
className='modal-prev-bar'
href='#'
onClick={this.handlePrev}>
<i className='image-control image-prev'/> <i className='image-control image-prev'/>
</a> </a>
); );
rightArrow = ( rightArrow = (
<a className='modal-next-bar' href='#' onClick={this.handleNext}> <a
ref='previewArrowRight'
className='modal-next-bar'
href='#'
onClick={this.handleNext}>
<i className='image-control image-next'/> <i className='image-control image-next'/>
</a> </a>
); );

Просмотреть файл

@@ -65,6 +65,9 @@
} }
.channel-intro-img { .channel-intro-img {
float:left; float:left;
img {
@include border-radius(100px);
}
} }
.channel-intro__title { .channel-intro__title {
font-weight:600; font-weight:600;

Просмотреть файл

@@ -50,6 +50,13 @@
@include clearfix; @include clearfix;
.modal-title { .modal-title {
float: left; float: left;
font-size: 17px;
line-height: 27px;
color: #f4f4f4;
.name {
font-weight: 600;
color: #fff;
}
} }
.modal-action { .modal-action {
padding: 0; padding: 0;

Просмотреть файл

@@ -1,3 +1,8 @@
.channel-header__info .popover-content {
max-height: 250px;
overflow: auto;
}
.user-popover { .user-popover {
cursor: pointer; cursor: pointer;
display: inline-block; display: inline-block;
@@ -13,4 +18,5 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: block; display: block;
} }

Просмотреть файл

@@ -38,24 +38,25 @@
height: 100%; height: 100%;
position: relative; position: relative;
overflow: auto; overflow: auto;
} }
.nav-pills__unread-indicator { .nav-pills__unread-indicator {
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
width: 70%; width: 72%;
background-color: darken($primary-color, 5%); color: #777;
color: white; background: #DCF0FF;
@include border-radius(50px);
margin: 0 auto; margin: 0 auto;
padding: 2px; padding: 3px 0 4px;
font-size: 13.5px;
text-align: center; text-align: center;
z-index: 1; z-index: 1;
} }
.nav-pills__unread-indicator-top { .nav-pills__unread-indicator-top {
top: 56px; top: 66px;
} }
.nav-pills__unread-indicator-bottom { .nav-pills__unread-indicator-bottom {
bottom: 0px; bottom: 0px;
@@ -124,7 +125,15 @@
} }
.channel-loading-gif { .channel-loading-gif {
height:15px; height: 15px;
width:15px; width: 15px;
margin-top:2px; margin-top: 2px;
}
.join-channel-loading-gif {
margin-top: 5px;
margin-left: 10px;
height: 25px;
width: 25px;
} }