summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile20
-rw-r--r--README.md9
-rw-r--r--STYLE-GUIDE.md4
-rw-r--r--api/file.go8
-rw-r--r--web/react/components/channel_header.jsx185
-rw-r--r--web/react/components/delete_channel_modal.jsx2
-rw-r--r--web/react/components/delete_post_modal.jsx2
-rw-r--r--web/react/components/edit_channel_modal.jsx2
-rw-r--r--web/react/components/edit_post_modal.jsx2
-rw-r--r--web/react/components/get_link_modal.jsx49
-rw-r--r--web/react/components/invite_member_modal.jsx270
-rw-r--r--web/react/components/mention_list.jsx189
-rw-r--r--web/react/components/new_channel.jsx139
-rw-r--r--web/react/components/post_list.jsx15
-rw-r--r--web/react/components/setting_picture.jsx30
-rw-r--r--web/react/components/sidebar.jsx233
-rw-r--r--web/react/components/sidebar_header.jsx117
-rw-r--r--web/react/components/sidebar_right_menu.jsx75
-rw-r--r--web/react/components/signup_user_complete.jsx200
-rw-r--r--web/react/components/team_settings_modal.jsx2
-rw-r--r--web/react/components/textbox.jsx161
-rw-r--r--web/react/components/user_profile.jsx3
-rw-r--r--web/react/components/user_settings.jsx257
-rw-r--r--web/react/components/user_settings_modal.jsx3
-rw-r--r--web/react/stores/post_store.jsx354
-rw-r--r--web/react/utils/constants.jsx2
-rw-r--r--web/sass-files/sass/partials/_base.scss10
-rw-r--r--web/sass-files/sass/partials/_files.scss61
-rw-r--r--web/sass-files/sass/partials/_get-link.scss6
-rw-r--r--web/sass-files/sass/partials/_headers.scss14
-rw-r--r--web/sass-files/sass/partials/_modal.scss9
-rw-r--r--web/sass-files/sass/partials/_navbar.scss1
-rw-r--r--web/sass-files/sass/partials/_popover.scss7
-rw-r--r--web/sass-files/sass/partials/_responsive.scss10
-rw-r--r--web/sass-files/sass/styles.scss1
-rw-r--r--web/static/config/config.js3
-rw-r--r--web/templates/channel.html4
37 files changed, 1338 insertions, 1121 deletions
diff --git a/Makefile b/Makefile
index 370289507..30babfa39 100644
--- a/Makefile
+++ b/Makefile
@@ -101,14 +101,6 @@ install:
docker start mattermost-mysql > /dev/null; \
fi
- @if [ $(shell docker ps -a | grep -ci mattermost-redis) -eq 0 ]; then \
- echo starting mattermost-redis; \
- docker run --name mattermost-redis -p 6379:6379 -d redis > /dev/null; \
- elif [ $(shell docker ps | grep -ci mattermost-redis) -eq 0 ]; then \
- echo restarting mattermost-redis; \
- docker start mattermost-redis > /dev/null; \
- fi
-
@cd web/react/ && npm install
check: install
@@ -159,12 +151,6 @@ clean:
docker rm -v mattermost-mysql > /dev/null; \
fi
- @if [ $(shell docker ps -a | grep -ci mattermost-redis) -eq 1 ]; then \
- echo stopping mattermost-redis; \
- docker stop mattermost-redis > /dev/null; \
- docker rm -v mattermost-redis > /dev/null; \
- fi
-
rm -rf web/react/node_modules
rm -f web/static/js/bundle*.js
rm -f web/static/css/styles.css
@@ -216,12 +202,6 @@ cleandb:
docker stop mattermost-mysql > /dev/null; \
docker rm -v mattermost-mysql > /dev/null; \
fi
-
- @if [ $(shell docker ps -a | grep -ci mattermost-redis) -eq 1 ]; then \
- docker stop mattermost-redis > /dev/null; \
- docker rm -v mattermost-redis > /dev/null; \
- fi
-
dist: install
@$(GO) build $(GOFLAGS) -i -a ./...
diff --git a/README.md b/README.md
index cf5868f08..5af82bb72 100644
--- a/README.md
+++ b/README.md
@@ -31,12 +31,11 @@ Local Machine Setup (Docker)
### Mac OSX ###
-1. Follow the instructions at: http://docs.docker.com/installation/mac/
- 1. Use the Boot2Docker command-line utility.
- 2. If you do command-line setup use: `boot2docker init eval “$(boot2docker shellinit)”`
+1. Install Boot2Docker using instructions at: http://docs.docker.com/installation/mac/
+ 1. Start Boot2Docker from the command line and run: `boot2docker init eval “$(boot2docker shellinit)”`
2. Get your Docker IP address with: `boot2docker ip`
-3. Add a line to your /etc/hosts that goes: `<Docker IP> dockerhost`
-4. Run: `boot2docker shellinit` and copy the export statements to your ~/.bash\_profile.
+3. Use `sudo nano /etc/hosts` to add `<Docker IP> dockerhost` to your /etc/hosts file
+4. Run: `boot2docker shellinit` and copy the export statements to your ~/.bash\_profile by running `sudo nano ~/.bash_profile`. Then run: `source ~/.bash_profile`
5. Run: `docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform`
6. When docker is done fetching the image, open http://dockerhost:8065/ in your browser.
diff --git a/STYLE-GUIDE.md b/STYLE-GUIDE.md
index e3fe2addf..0da0a14f8 100644
--- a/STYLE-GUIDE.md
+++ b/STYLE-GUIDE.md
@@ -1,6 +1,6 @@
# Mattermost Style Guide
-1. [GO](#go)
+1. [Go](#go)
2. [Javascript](#javascript)
3. [React-JSX](#react-jsx)
@@ -159,7 +159,7 @@ This is an abridged version of the [Airbnb React/JSX Style Guide](https://github
- Property names use camelCase.
- React component names use CapitalCamelCase.
-- Do not use an understore for internal methods in a react component.
+- Do not use an underscore for internal methods in a react component.
```xml
// Correct
diff --git a/api/file.go b/api/file.go
index 4ec421eb9..bf1c59422 100644
--- a/api/file.go
+++ b/api/file.go
@@ -15,6 +15,8 @@ import (
"github.com/nfnt/resize"
_ "golang.org/x/image/bmp"
"image"
+ "image/color"
+ "image/draw"
_ "image/gif"
"image/jpeg"
"io"
@@ -138,6 +140,12 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
return
}
+ // Remove transparency due to JPEG's lack of support of it
+ temp := image.NewRGBA(img.Bounds())
+ draw.Draw(temp, temp.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src)
+ draw.Draw(temp, temp.Bounds(), img, img.Bounds().Min, draw.Over)
+ img = temp
+
// Create thumbnail
go func() {
thumbWidth := float64(utils.Cfg.ImageSettings.ThumbnailWidth)
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 76dbe370b..90a776791 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -1,13 +1,11 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-
var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var PostStore = require('../stores/post_store.jsx');
-var SocketStore = require('../stores/socket_store.jsx')
-var UserProfile = require( './user_profile.jsx' );
-var NavbarSearchBox =require('./search_bar.jsx');
+var SocketStore = require('../stores/socket_store.jsx');
+var NavbarSearchBox = require('./search_bar.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var Client = require('../utils/client.jsx');
var utils = require('../utils/utils.jsx');
@@ -21,23 +19,28 @@ var PopoverListMembers = React.createClass({
componentDidMount: function() {
var originalLeave = $.fn.popover.Constructor.prototype.leave;
$.fn.popover.Constructor.prototype.leave = function(obj) {
- var self = obj instanceof this.constructor ? obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type);
+ var selfObj;
+ if (obj instanceof this.constructor) {
+ selfObj = obj;
+ } else {
+ selfObj = $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type);
+ }
originalLeave.call(this, obj);
- if (obj.currentTarget && self.$tip) {
- self.$tip.one('mouseenter', function() {
- clearTimeout(self.timeout);
- self.$tip.one('mouseleave', function() {
- $.fn.popover.Constructor.prototype.leave.call(self, self);
+ if (obj.currentTarget && selfObj.$tip) {
+ selfObj.$tip.one('mouseenter', function() {
+ clearTimeout(selfObj.timeout);
+ selfObj.$tip.one('mouseleave', function() {
+ $.fn.popover.Constructor.prototype.leave.call(selfObj, selfObj);
});
- })
+ });
}
};
- $("#member_popover").popover({placement : 'bottom', trigger: 'click', html: true});
- $('body').on('click', function (e) {
- if ($(e.target.parentNode.parentNode)[0] !== $("#member_popover")[0] && $(e.target).parents('.popover.in').length === 0) {
- $("#member_popover").popover('hide');
+ $('#member_popover').popover({placement: 'bottom', trigger: 'click', html: true});
+ $('body').on('click', function(e) {
+ if ($(e.target.parentNode.parentNode)[0] !== $('#member_popover')[0] && $(e.target).parents('.popover.in').length === 0) {
+ $('#member_popover').popover('hide');
}
});
},
@@ -45,22 +48,27 @@ var PopoverListMembers = React.createClass({
render: function() {
var popoverHtml = '';
var members = this.props.members;
- var count = (members.length > 20) ? "20+" : (members.length || '-');
+ var count;
+ if (members.length > 20) {
+ count = '20+';
+ } else {
+ count = members.length || '-';
+ }
if (members) {
- members.sort(function(a,b) {
+ members.sort(function(a, b) {
return a.username.localeCompare(b.username);
});
members.forEach(function(m) {
- popoverHtml += "<div class='text--nowrap'>" + m.username + "</div>";
+ popoverHtml += "<div class='text--nowrap'>" + m.username + '</div>';
});
}
return (
- <div id="member_popover" data-toggle="popover" data-content={popoverHtml} data-original-title="Members" >
- <div id="member_tooltip" data-toggle="tooltip" title="View Channel Members">
- {count} <span className="glyphicon glyphicon-user" aria-hidden="true"></span>
+ <div id='member_popover' data-toggle='popover' data-content={popoverHtml} data-original-title='Members' >
+ <div id='member_tooltip' data-placement='left' data-toggle='tooltip' title='View Channel Members'>
+ {count} <span className='glyphicon glyphicon-user' aria-hidden='true'></span>
</div>
</div>
);
@@ -68,53 +76,53 @@ var PopoverListMembers = React.createClass({
});
function getStateFromStores() {
- return {
- channel: ChannelStore.getCurrent(),
- memberChannel: ChannelStore.getCurrentMember(),
- memberTeam: UserStore.getCurrentUser(),
- users: ChannelStore.getCurrentExtraInfo().members,
- search_visible: PostStore.getSearchResults() != null
- };
+ return {
+ channel: ChannelStore.getCurrent(),
+ memberChannel: ChannelStore.getCurrentMember(),
+ memberTeam: UserStore.getCurrentUser(),
+ users: ChannelStore.getCurrentExtraInfo().members,
+ searchVisible: PostStore.getSearchResults() != null
+ };
}
module.exports = React.createClass({
displayName: 'ChannelHeader',
componentDidMount: function() {
- ChannelStore.addChangeListener(this._onChange);
- ChannelStore.addExtraInfoChangeListener(this._onChange);
- PostStore.addSearchChangeListener(this._onChange);
- UserStore.addChangeListener(this._onChange);
- SocketStore.addChangeListener(this._onSocketChange);
+ ChannelStore.addChangeListener(this.onListenerChange);
+ ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
+ PostStore.addSearchChangeListener(this.onListenerChange);
+ UserStore.addChangeListener(this.onListenerChange);
+ SocketStore.addChangeListener(this.onSocketChange);
},
componentWillUnmount: function() {
- ChannelStore.removeChangeListener(this._onChange);
- ChannelStore.removeExtraInfoChangeListener(this._onChange);
- PostStore.removeSearchChangeListener(this._onChange);
- UserStore.addChangeListener(this._onChange);
+ ChannelStore.removeChangeListener(this.onListenerChange);
+ ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
+ PostStore.removeSearchChangeListener(this.onListenerChange);
+ UserStore.addChangeListener(this.onListenerChange);
},
- _onChange: function() {
+ onListenerChange: function() {
var newState = getStateFromStores();
if (!utils.areStatesEqual(newState, this.state)) {
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', html: true, delay: {show: 500, hide: 500}});
},
- _onSocketChange: function(msg) {
- if (msg.action === "new_user") {
+ onSocketChange: function(msg) {
+ if (msg.action === 'new_user') {
AsyncClient.getChannelExtraInfo(true);
}
},
getInitialState: function() {
return getStateFromStores();
},
- handleLeave: function(e) {
+ handleLeave: function() {
Client.leaveChannel(this.state.channel.id,
- function(data) {
+ function() {
var townsquare = ChannelStore.getByName('town-square');
utils.switchChannel(townsquare);
},
function(err) {
- AsyncClient.dispatchError(err, "handleLeave");
+ AsyncClient.dispatchError(err, 'handleLeave');
}
);
},
@@ -123,9 +131,16 @@ module.exports = React.createClass({
var user = UserStore.getCurrentUser();
- var terms = "";
+ var terms = '';
if (user.notify_props && user.notify_props.mention_keys) {
- terms = UserStore.getCurrentMentionKeys().join(' ');
+ var termKeys = UserStore.getCurrentMentionKeys();
+ if (user.notify_props.all === 'true' && termKeys.indexOf('@all') !== -1) {
+ termKeys.splice(termKeys.indexOf('@all'), 1);
+ }
+ if (user.notify_props.channel === 'true' && termKeys.indexOf('@channel') !== -1) {
+ termKeys.splice(termKeys.indexOf('@channel'), 1);
+ }
+ terms = termKeys.join(' ');
}
AppDispatcher.handleServerAction({
@@ -135,81 +150,89 @@ module.exports = React.createClass({
is_mention_search: true
});
},
-
render: function() {
-
if (this.state.channel == null) {
return null;
}
var channel = this.state.channel;
- var description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true});
+ var description = utils.textToJsx(channel.description, {singleline: true, noMentionHighlight: true});
var popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
var channelTitle = channel.display_name;
var currentId = UserStore.getCurrentId();
- var isAdmin = this.state.memberChannel.roles.indexOf("admin") > -1 || this.state.memberTeam.roles.indexOf("admin") > -1;
+ var isAdmin = this.state.memberChannel.roles.indexOf('admin') > -1 || this.state.memberTeam.roles.indexOf('admin') > -1;
var isDirect = (this.state.channel.type === 'D');
if (isDirect) {
if (this.state.users.length > 1) {
- var contact = this.state.users[((this.state.users[0].id === currentId) ? 1 : 0)];
+ var contact;
+ if (this.state.users[0].id === currentId) {
+ contact = this.state.users[1];
+ } else {
+ contact = this.state.users[0];
+ }
channelTitle = contact.nickname || contact.username;
}
}
+ var channelTerm = 'Channel';
+ if (channel.type === 'P') {
+ channelTerm = 'Group';
+ }
+
return (
- <table className="channel-header alt">
+ <table className='channel-header alt'>
<tr>
<th>
- <div className="channel-header__info">
- <div className="dropdown">
- <a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
- <strong className="heading">{channelTitle} </strong>
- <span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
+ <div className='channel-header__info'>
+ <div className='dropdown'>
+ <a href='#' className='dropdown-toggle theme' type='button' id='channel_header_dropdown' data-toggle='dropdown' aria-expanded='true'>
+ <strong className='heading'>{channelTitle} </strong>
+ <span className='glyphicon glyphicon-chevron-down header-dropdown__icon'></span>
</a>
- { !isDirect ?
- <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={channel.id} href="#">View Info</a></li>
- { !ChannelStore.isDefault(channel) ?
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
+ {!isDirect ?
+ <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={channel.id} href='#'>View Info</a></li>
+ {!ChannelStore.isDefault(channel) ?
+ <li role='presentation'><a role='menuitem' data-toggle='modal' data-target='#channel_invite' href='#'>Add Members</a></li>
: null
}
- { isAdmin && !ChannelStore.isDefault(channel) ?
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
+ {isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role='presentation'><a role='menuitem' data-toggle='modal' data-target='#channel_members' href='#'>Manage Members</a></li>
: null
}
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li>
- { isAdmin && !ChannelStore.isDefault(channel) ?
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={channel.display_name} data-name={channel.name} data-channelid={channel.id}>Rename Channel...</a></li>
+ <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set {channelTerm} Description...</a></li>
+ <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#channel_notifications' data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li>
+ {isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#rename_channel' data-display={channel.display_name} data-name={channel.name} data-channelid={channel.id}>Rename {channelTerm}...</a></li>
: null
}
- { isAdmin && !ChannelStore.isDefault(channel) ?
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={channel.display_name} data-channelid={channel.id}>Delete Channel...</a></li>
+ {isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#delete_channel' data-title={channel.display_name} data-channelid={channel.id}>Delete {channelTerm}...</a></li>
: null
}
- { !ChannelStore.isDefault(channel) ?
- <li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
+ {!ChannelStore.isDefault(channel) ?
+ <li role='presentation'><a role='menuitem' href='#' onClick={this.handleLeave}>Leave {channelTerm}</a></li>
: null
}
</ul>
:
- <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
+ <ul className='dropdown-menu' role='menu' aria-labelledby='channel_header_dropdown'>
+ <li role='presentation'><a role='menuitem' href='#' data-toggle='modal' data-target='#edit_channel' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
</ul>
}
</div>
- <div data-toggle="popover" data-content={popoverContent} className="description">{description}</div>
+ <div data-toggle='popover' data-content={popoverContent} className='description'>{description}</div>
</div>
</th>
<th><PopoverListMembers members={this.state.users} channelId={channel.id} /></th>
- <th className="search-bar__container"><NavbarSearchBox /></th>
+ <th className='search-bar__container'><NavbarSearchBox /></th>
<th>
- <div className="dropdown channel-header__links">
- <a href="#" className="dropdown-toggle theme" type="button" id="channel_header_right_dropdown" data-toggle="dropdown" aria-expanded="true">
- <span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON }} /> </a>
- <ul className="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="channel_header_right_dropdown">
- <li role="presentation"><a role="menuitem" href="#" onClick={this.searchMentions}>Recent Mentions</a></li>
+ <div className='dropdown channel-header__links'>
+ <a href='#' className='dropdown-toggle theme' type='button' id='channel_header_right_dropdown' data-toggle='dropdown' aria-expanded='true'>
+ <span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} /> </a>
+ <ul className='dropdown-menu dropdown-menu-right' role='menu' aria-labelledby='channel_header_right_dropdown'>
+ <li role='presentation'><a role='menuitem' href='#' onClick={this.searchMentions}>Recent Mentions</a></li>
</ul>
</div>
</th>
diff --git a/web/react/components/delete_channel_modal.jsx b/web/react/components/delete_channel_modal.jsx
index 64ceec450..589737271 100644
--- a/web/react/components/delete_channel_modal.jsx
+++ b/web/react/components/delete_channel_modal.jsx
@@ -34,7 +34,7 @@ module.exports = React.createClass({
var channelType = ChannelStore.getCurrent() && ChannelStore.getCurrent().type === 'P' ? "private group" : "channel"
return (
- <div className="modal fade" ref="modal" id="delete_channel" role="dialog" aria-hidden="true">
+ <div className="modal fade" ref="modal" id="delete_channel" role="dialog" tabIndex="-1" aria-hidden="true">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx
index f0cb809af..1b6a7e162 100644
--- a/web/react/components/delete_post_modal.jsx
+++ b/web/react/components/delete_post_modal.jsx
@@ -82,7 +82,7 @@ module.exports = React.createClass({
var error = this.state.error ? <div className='form-group has-error'><label className='control-label'>{ this.state.error }</label></div> : null;
return (
- <div className="modal fade" id="delete_post" ref="modal" role="dialog" aria-hidden="true">
+ <div className="modal fade" id="delete_post" ref="modal" role="dialog" tabIndex="-1" aria-hidden="true">
<div className="modal-dialog modal-push-down">
<div className="modal-content">
<div className="modal-header">
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
index 1b0cc185f..06d7fc3e8 100644
--- a/web/react/components/edit_channel_modal.jsx
+++ b/web/react/components/edit_channel_modal.jsx
@@ -51,7 +51,7 @@ module.exports = React.createClass({
var server_error = this.state.server_error ? <div className='form-group has-error'><br/><label className='control-label'>{ this.state.server_error }</label></div> : null;
return (
- <div className="modal fade" ref="modal" id="edit_channel" role="dialog" aria-hidden="true">
+ <div className="modal fade" ref="modal" id="edit_channel" role="dialog" tabIndex="-1" aria-hidden="true">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
index 21b75bb6e..064d3fa94 100644
--- a/web/react/components/edit_post_modal.jsx
+++ b/web/react/components/edit_post_modal.jsx
@@ -71,7 +71,7 @@ module.exports = React.createClass({
var error = this.state.error ? <div className='form-group has-error'><label className='control-label'>{ this.state.error }</label></div> : null;
return (
- <div className="modal fade edit-modal" ref="modal" id="edit_post" role="dialog" aria-hidden="true">
+ <div className="modal fade edit-modal" ref="modal" id="edit_post" role="dialog" tabIndex="-1" aria-hidden="true">
<div className="modal-dialog modal-push-down">
<div className="modal-content">
<div className="modal-header">
diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx
index af5314e64..ea22ad0f3 100644
--- a/web/react/components/get_link_modal.jsx
+++ b/web/react/components/get_link_modal.jsx
@@ -10,46 +10,57 @@ ZeroClipboardMixin.ZeroClipboard.config({
module.exports = React.createClass({
zeroclipboardElementsSelector: '[data-copy-btn]',
- mixins: [ ZeroClipboardMixin ],
+ mixins: [ZeroClipboardMixin],
componentDidMount: function() {
var self = this;
- if(this.refs.modal) {
+ if (this.refs.modal) {
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
var button = e.relatedTarget;
- self.setState({title: $(button).attr('data-title'), value: $(button).attr('data-value') });
+ self.setState({title: $(button).attr('data-title'), value: $(button).attr('data-value')});
+ });
+ $(this.refs.modal.getDOMNode()).on('hide.bs.modal', function() {
+ self.setState({copiedLink: false});
});
}
},
getInitialState: function() {
- return { };
+ return {copiedLink: false};
+ },
+ handleClick: function() {
+ this.setState({copiedLink: true});
},
render: function() {
- var currentUser = UserStore.getCurrentUser()
+ var currentUser = UserStore.getCurrentUser();
+ var copyLinkConfirm = null;
+
+ if (this.state.copiedLink) {
+ copyLinkConfirm = <p className='copy-link-confirm'>Link copied to clipboard.</p>;
+ }
if (currentUser != null) {
return (
- <div className="modal fade" ref="modal" id="get_link" 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"><span aria-hidden="true">&times;</span></button>
- <h4 className="modal-title" id="myModalLabel">{this.state.title} Link</h4>
+ <div className='modal fade' ref='modal' id='get_link' 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'><span aria-hidden='true'>&times;</span></button>
+ <h4 className='modal-title' id='myModalLabel'>{this.state.title} Link</h4>
</div>
- <div className="modal-body">
- <p>{"The link below is used for open " + strings.TeamPlural + " or if you allowed your " + strings.Team + " members to sign up using their " + strings.Company + " email addresses."}
+ <div className='modal-body'>
+ <p>{'The link below is used for open ' + strings.TeamPlural + ' or if you allowed your ' + strings.Team + ' members to sign up using their ' + strings.Company + ' email addresses.'}
</p>
- <textarea className="form-control no-resize" readOnly="true" value={this.state.value}></textarea>
+ <textarea className='form-control no-resize' readOnly='true' value={this.state.value}></textarea>
</div>
- <div className="modal-footer">
- <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
- <button data-copy-btn type="button" className="btn btn-primary pull-left" data-clipboard-text={this.state.value}>Copy Link</button>
+ <div className='modal-footer'>
+ <button type='button' className='btn btn-default' data-dismiss='modal'>Close</button>
+ <button data-copy-btn='true' type='button' className='btn btn-primary pull-left' onClick={this.handleClick} data-clipboard-text={this.state.value}>Copy Link</button>
+ {copyLinkConfirm}
</div>
</div>
</div>
</div>
);
- } else {
- return <div/>;
}
+ return <div/>;
}
});
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index fed96b50a..3eca79bae 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
var utils = require('../utils/utils.jsx');
-var Client =require('../utils/client.jsx');
+var Client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
var ConfirmModal = require('./confirm_modal.jsx');
@@ -15,20 +15,19 @@ module.exports = React.createClass({
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;
+ var notEmpty = false;
+ for (var i = 0; i < self.state.inviteIds.length; i++) {
+ var index = self.state.inviteIds[i];
+ if (self.refs['email' + index].getDOMNode().value.trim() !== '') {
+ notEmpty = true;
break;
}
}
- if (not_empty) {
+ if (notEmpty) {
$('#confirm_invite_modal').modal('show');
e.preventDefault();
}
-
});
$('#invite_member').on('hidden.bs.modal', function() {
@@ -36,52 +35,54 @@ module.exports = React.createClass({
});
},
handleSubmit: function(e) {
- var invite_ids = this.state.invite_ids;
- var count = invite_ids.length;
+ var inviteIds = this.state.inviteIds;
+ var count = inviteIds.length;
var invites = [];
- var email_errors = this.state.email_errors;
- var first_name_errors = this.state.first_name_errors;
- var last_name_errors = this.state.last_name_errors;
+ var emailErrors = this.state.emailErrors;
+ var firstNameErrors = this.state.firstNameErrors;
+ var lastNameErrors = this.state.lastNameErrors;
var valid = true;
for (var i = 0; i < count; i++) {
- var index = invite_ids[i];
+ var index = inviteIds[i];
var invite = {};
- invite.email = this.refs["email"+index].getDOMNode().value.trim();
+ invite.email = this.refs['email' + index].getDOMNode().value.trim();
if (!invite.email || !utils.isEmail(invite.email)) {
- email_errors[index] = "Please enter a valid email address";
+ emailErrors[index] = 'Please enter a valid email address';
valid = false;
} else {
- email_errors[index] = "";
+ emailErrors[index] = '';
}
if (config.AllowInviteNames) {
- invite.first_name = this.refs["first_name"+index].getDOMNode().value.trim();
- if (!invite.first_name && config.RequireInviteNames) {
- first_name_errors[index] = "This is a required field";
+ invite.firstName = this.refs['first_name' + index].getDOMNode().value.trim();
+ if (!invite.firstName && config.RequireInviteNames) {
+ firstNameErrors[index] = 'This is a required field';
valid = false;
} else {
- first_name_errors[index] = "";
+ firstNameErrors[index] = '';
}
- invite.last_name = this.refs["last_name"+index].getDOMNode().value.trim();
- if (!invite.last_name && config.RequireInviteNames) {
- last_name_errors[index] = "This is a required field";
+ invite.lastName = this.refs['last_name' + index].getDOMNode().value.trim();
+ if (!invite.lastName && config.RequireInviteNames) {
+ lastNameErrors[index] = 'This is a required field';
valid = false;
} else {
- last_name_errors[index] = "";
+ lastNameErrors[index] = '';
}
}
invites.push(invite);
}
- this.setState({ email_errors: email_errors, first_name_errors: first_name_errors, last_name_errors: last_name_errors });
+ this.setState({emailErrors: emailErrors, firstNameErrors: firstNameErrors, lastNameErrors: lastNameErrors});
- if (!valid || invites.length === 0) return;
+ if (!valid || invites.length === 0) {
+ return;
+ }
- var data = {}
- data["invites"] = invites;
+ var data = {};
+ data.invites = invites;
Client.inviteMembers(data,
function() {
@@ -89,146 +90,177 @@ module.exports = React.createClass({
$(this.refs.modal.getDOMNode()).modal('hide');
}.bind(this),
function(err) {
- if (err.message === "This person is already on your team") {
- email_errors[err.detailed_error] = err.message;
- this.setState({ email_errors: email_errors });
+ if (err.message === 'This person is already on your team') {
+ emailErrors[err.detailed_error] = err.message;
+ this.setState({emailErrors: emailErrors});
+ } else {
+ this.setState({serverError: err.message});
}
- else
- this.setState({ server_error: err.message});
}.bind(this)
);
-
},
componentDidUpdate: function() {
$(this.refs.modalBody.getDOMNode()).css('max-height', $(window).height() - 200);
$(this.refs.modalBody.getDOMNode()).css('overflow-y', 'scroll');
},
addInviteFields: function() {
- var count = this.state.id_count + 1;
- var invite_ids = this.state.invite_ids;
- invite_ids.push(count);
- this.setState({ invite_ids: invite_ids, id_count: count });
+ var count = this.state.idCount + 1;
+ var inviteIds = this.state.inviteIds;
+ inviteIds.push(count);
+ this.setState({inviteIds: inviteIds, idCount: count});
},
clearFields: function() {
- var invite_ids = this.state.invite_ids;
+ var inviteIds = this.state.inviteIds;
- for (var i = 0; i < invite_ids.length; i++) {
- var index = invite_ids[i];
- this.refs["email"+index].getDOMNode().value = "";
+ for (var i = 0; i < inviteIds.length; i++) {
+ var index = inviteIds[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.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: {}
+ inviteIds: [0],
+ idCount: 0,
+ emailErrors: {},
+ firstNameErrors: {},
+ lastNameErrors: {}
});
},
removeInviteFields: function(index) {
- var count = this.state.id_count;
- var invite_ids = this.state.invite_ids;
- var i = invite_ids.indexOf(index);
- 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 });
+ var count = this.state.idCount;
+ var inviteIds = this.state.inviteIds;
+ var i = inviteIds.indexOf(index);
+ if (i > -1) {
+ inviteIds.splice(i, 1);
+ }
+ if (!inviteIds.length) {
+ inviteIds.push(++count);
+ }
+ this.setState({inviteIds: inviteIds, idCount: count});
},
getInitialState: function() {
return {
- invite_ids: [0],
- id_count: 0,
- email_errors: {},
- first_name_errors: {},
- last_name_errors: {}
+ inviteIds: [0],
+ idCount: 0,
+ emailErrors: {},
+ firstNameErrors: {},
+ lastNameErrors: {}
};
},
render: function() {
- var currentUser = UserStore.getCurrentUser()
+ var currentUser = UserStore.getCurrentUser();
if (currentUser != null) {
- var invite_sections = [];
- var invite_ids = this.state.invite_ids;
- var self = this;
- for (var i = 0; i < invite_ids.length; i++) {
- var index = invite_ids[i];
- var email_error = this.state.email_errors[index] ? <label className='control-label'>{ this.state.email_errors[index] }</label> : null;
- var first_name_error = this.state.first_name_errors[index] ? <label className='control-label'>{ this.state.first_name_errors[index] }</label> : null;
- var last_name_error = this.state.last_name_errors[index] ? <label className='control-label'>{ this.state.last_name_errors[index] }</label> : null;
-
- invite_sections[index] = (
- <div key={"key" + index}>
- <div>
- <button type="button" className="btn btn-link remove__member" onClick={this.removeInviteFields.bind(this, index)}><span className="fa fa-trash"></span></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 }
- </div>
- <div className="row--invite">
- { config.AllowInviteNames ?
- <div className="col-sm-6">
- <div className={ first_name_error ? "form-group has-error" : "form-group" }>
- <input type="text" className="form-control" ref={"first_name"+index} placeholder="First name" maxLength="64" />
- { first_name_error }
- </div>
- </div>
- : "" }
- { config.AllowInviteNames ?
- <div className="col-sm-6">
- <div className={ last_name_error ? "form-group has-error" : "form-group" }>
- <input type="text" className="form-control" ref={"last_name"+index} placeholder="Last name" maxLength="64" />
- { last_name_error }
- </div>
- </div>
- : "" }
+ var inviteSections = [];
+ var inviteIds = this.state.inviteIds;
+ for (var i = 0; i < inviteIds.length; i++) {
+ var index = inviteIds[i];
+ var emailError = null;
+ if (this.state.emailErrors[index]) {
+ emailError = <label className='control-label'>{this.state.emailErrors[index]}</label>;
+ }
+ var firstNameError = null;
+ if (this.state.firstNameErrors[index]) {
+ firstNameError = <label className='control-label'>{this.state.firstNameErrors[index]}</label>;
+ }
+ var lastNameError = null;
+ if (this.state.lastNameErrors[index]) {
+ lastNameError = <label className='control-label'>{this.state.lastNameErrors[index]}</label>;
+ }
+
+ var removeButton = null;
+ if (index) {
+ removeButton = (<div>
+ <button type='button' className='btn btn-link remove__member' onClick={this.removeInviteFields.bind(this, index)}><span className='fa fa-trash'></span></button>
+ </div>);
+ }
+ var emailClass = 'form-group invite';
+ if (emailError) {
+ emailClass += ' has-error';
+ }
+
+ var nameFields = null;
+ if (config.AllowInviteNames) {
+ var firstNameClass = 'form-group';
+ if (firstNameError) {
+ firstNameClass += ' has-error';
+ }
+ var lastNameClass = 'form-group';
+ if (lastNameError) {
+ lastNameClass += ' has-error';
+ }
+ nameFields = (<div className='row--invite'>
+ <div className='col-sm-6'>
+ <div className={firstNameClass}>
+ <input type='text' className='form-control' ref={'first_name' + index} placeholder='First name' maxLength='64' />
+ {firstNameError}
+ </div>
+ </div>
+ <div className='col-sm-6'>
+ <div className={lastNameClass}>
+ <input type='text' className='form-control' ref={'last_name' + index} placeholder='Last name' maxLength='64' />
+ {lastNameError}
+ </div>
+ </div>
+ </div>);
+ }
+
+ inviteSections[index] = (
+ <div key={'key' + index}>
+ {removeButton}
+ <div className={emailClass}>
+ <input onKeyUp={this.displayNameKeyUp} type='text' ref={'email' + index} className='form-control' placeholder='email@domain.com' maxLength='64' />
+ {emailError}
</div>
+ {nameFields}
</div>
);
}
- 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>;
+ }
return (
<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 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 }
+ <div ref='modalBody' className='modal-body'>
+ <form role='form'>
+ {inviteSections}
</form>
- { server_error }
- <button type="button" className="btn btn-default" onClick={this.addInviteFields}>Add another</button>
+ {serverError}
+ <button type='button' className='btn btn-default' onClick={this.addInviteFields}>Add another</button>
<br/>
<br/>
<span>People invited automatically join Town Square channel.</span>
</div>
- <div className="modal-footer">
- <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button>
- <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Send Invitations</button>
+ <div className='modal-footer'>
+ <button type='button' className='btn btn-default' data-dismiss='modal'>Cancel</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"
+ 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 {
- return <div/>;
}
+ return <div/>;
}
});
diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx
index 71a6083d2..f562cfb29 100644
--- a/web/react/components/mention_list.jsx
+++ b/web/react/components/mention_list.jsx
@@ -15,81 +15,81 @@ var MAX_ITEMS_IN_LIST = 25;
var ITEM_HEIGHT = 36;
module.exports = React.createClass({
- displayName: "MentionList",
+ displayName: 'MentionList',
componentDidMount: function() {
- PostStore.addMentionDataChangeListener(this._onChange);
+ PostStore.addMentionDataChangeListener(this.onListenerChange);
var self = this;
- $('body').on('keydown.mentionlist', '#'+this.props.id,
+ $('.post-right__scroll').scroll(function(){
+ if($('.mentions--top').length){
+ $('#reply_mention_tab .mentions--top').css({ bottom: $(window).height() - $('.post-right__scroll #reply_textbox').offset().top });
+ }
+ });
+
+ $('body').on('keydown.mentionlist', '#' + this.props.id,
function(e) {
- if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 13 || e.which === 9)) {
+ if (!self.isEmpty() && self.state.mentionText !== '-1' && (e.which === 13 || e.which === 9)) {
e.stopPropagation();
e.preventDefault();
self.addCurrentMention();
- }
- else if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 38 || e.which === 40)) {
+ } else if (!self.isEmpty() && self.state.mentionText !== '-1' && (e.which === 38 || e.which === 40)) {
e.stopPropagation();
e.preventDefault();
- var tempSelectedMention = -1;
- if (e.which === 38) {
- if (self.getSelection(self.state.selectedMention - 1))
- self.setState({ selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username });
- else {
- while (self.getSelection(++tempSelectedMention))
- ; //Need to find the top of the list
- self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username });
+ if (e.which === 38) {
+ if (self.getSelection(self.state.selectedMention - 1)) {
+ self.setState({selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username});
+ }
+ } else if (e.which === 40) {
+ if (self.getSelection(self.state.selectedMention + 1)) {
+ self.setState({selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username});
}
- }
- else if (e.which === 40) {
- if (self.getSelection(self.state.selectedMention + 1))
- self.setState({ selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username });
- else
- self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username });
}
- self.scrollToMention(e.which, tempSelectedMention);
+ self.scrollToMention(e.which);
}
}
);
$(document).click(function(e) {
- if (!($('#'+self.props.id).is(e.target) || $('#'+self.props.id).has(e.target).length ||
- ('mentionlist' in self.refs && $(self.refs['mentionlist'].getDOMNode()).has(e.target).length))) {
- self.setState({mentionText: "-1"})
+ if (!($('#' + self.props.id).is(e.target) || $('#' + self.props.id).has(e.target).length ||
+ ('mentionlist' in self.refs && $(self.refs.mentionlist.getDOMNode()).has(e.target).length))) {
+ self.setState({mentionText: '-1'});
}
});
},
componentWillUnmount: function() {
- PostStore.removeMentionDataChangeListener(this._onChange);
- $('body').off('keydown.mentionlist', '#'+this.props.id);
+ PostStore.removeMentionDataChangeListener(this.onListenerChange);
+ $('body').off('keydown.mentionlist', '#' + this.props.id);
},
componentDidUpdate: function() {
- if (this.state.mentionText != "-1") {
- if (this.state.selectedUsername !== "" && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) {
+ if (this.state.mentionText !== '-1') {
+ if (this.state.selectedUsername !== '' && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) {
var tempSelectedMention = -1;
var foundMatch = false;
while (tempSelectedMention < this.state.selectedMention && this.getSelection(++tempSelectedMention)) {
if (this.state.selectedUsername === this.refs['mention' + tempSelectedMention].props.username) {
- this.setState({ selectedMention: tempSelectedMention });
+ this.setState({selectedMention: tempSelectedMention});
foundMatch = true;
break;
}
}
if (this.getSelection(0) && !foundMatch) {
- this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username });
+ this.setState({selectedMention: 0, selectedUsername: this.refs.mention0.props.username});
}
}
- }
- else if (this.state.selectedMention !== 0) {
- this.setState({ selectedMention: 0, selectedUsername: "" });
+ } else if (this.state.selectedMention !== 0) {
+ this.setState({selectedMention: 0, selectedUsername: ''});
}
},
- _onChange: function(id, mentionText, excludeList) {
- if (id !== this.props.id) return;
+ onListenerChange: function(id, mentionText) {
+ if (id !== this.props.id) {
+ return;
+ }
var newState = this.state;
- if (mentionText != null) newState.mentionText = mentionText;
- if (excludeList != null) newState.excludeUsers = excludeList;
+ if (mentionText != null) {
+ newState.mentionText = mentionText;
+ }
this.setState(newState);
},
@@ -100,63 +100,61 @@ module.exports = React.createClass({
username: name
});
- this.setState({ mentionText: '-1' });
+ this.setState({mentionText: '-1'});
},
handleMouseEnter: function(listId) {
- this.setState({ selectedMention: listId, selectedUsername: this.refs['mention' + listId].props.username });
+ this.setState({selectedMention: listId, selectedUsername: this.refs['mention' + listId].props.username});
},
getSelection: function(listId) {
- if (!this.refs['mention' + listId])
+ if (!this.refs['mention' + listId]) {
return false;
- else
- return true;
+ }
+ return true;
},
addCurrentMention: function() {
- if (!this.getSelection(this.state.selectedMention))
+ if (!this.getSelection(this.state.selectedMention)) {
this.addFirstMention();
- else
+ } else {
this.refs['mention' + this.state.selectedMention].handleClick();
+ }
},
addFirstMention: function() {
- if (!this.refs.mention0) return;
+ if (!this.refs.mention0) {
+ return;
+ }
this.refs.mention0.handleClick();
},
isEmpty: function() {
return (!this.refs.mention0);
},
- scrollToMention: function(keyPressed, ifLoopUp) {
- var direction = keyPressed === 38 ? "up" : "down";
+ scrollToMention: function(keyPressed) {
+ var direction;
+ if (keyPressed === 38) {
+ direction = 'up';
+ } else {
+ direction = 'down';
+ }
var scrollAmount = 0;
- if (direction === "up" && ifLoopUp !== -1)
- scrollAmount = $("#mentionsbox").height() * 100; //Makes sure that it scrolls all the way to the bottom
- else if (direction === "down" && this.state.selectedMention === 0)
- scrollAmount = 0;
- else if (direction === "up")
- scrollAmount = "-=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5);
- else if (direction === "down")
- scrollAmount = "+=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5);
+ if (direction === 'up') {
+ scrollAmount = '-=' + ($('#' + this.refs['mention' + this.state.selectedMention].props.id + '_mentions').innerHeight() - 5);
+ } else if (direction === 'down') {
+ scrollAmount = '+=' + ($('#' + this.refs['mention' + this.state.selectedMention].props.id + '_mentions').innerHeight() - 5);
+ }
- $("#mentionsbox").animate({
+ $('#mentionsbox').animate({
scrollTop: scrollAmount
}, 75);
},
- alreadyMentioned: function(username) {
- var excludeUsers = this.state.excludeUsers;
- for (var i = 0; i < excludeUsers.length; i++) {
- if (excludeUsers[i] === username) {
- return true;
- }
- }
- return false;
- },
getInitialState: function() {
- return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" };
+ return {excludeUsers: [], mentionText: '-1', selectedMention: 0, selectedUsername: ''};
},
render: function() {
var self = this;
var mentionText = this.state.mentionText;
- if (mentionText === '-1') return null;
+ if (mentionText === '-1') {
+ return null;
+ }
var profiles = UserStore.getActiveOnlyProfiles();
var users = [];
@@ -165,32 +163,35 @@ module.exports = React.createClass({
}
var all = {};
- all.username = "all";
- all.nickname = "";
- all.secondary_text = "Notifies everyone in the team";
- all.id = "allmention";
+ all.username = 'all';
+ all.nickname = '';
+ all.secondary_text = 'Notifies everyone in the team';
+ all.id = 'allmention';
users.push(all);
var channel = {};
- channel.username = "channel";
- channel.nickname = "";
- channel.secondary_text = "Notifies everyone in the channel";
- channel.id = "channelmention";
+ channel.username = 'channel';
+ channel.nickname = '';
+ channel.secondary_text = 'Notifies everyone in the channel';
+ channel.id = 'channelmention';
users.push(channel);
- users.sort(function(a,b) {
- if (a.username < b.username) return -1;
- if (a.username > b.username) return 1;
+ users.sort(function(a, b) {
+ if (a.username < b.username) {
+ return -1;
+ }
+ if (a.username > b.username) {
+ return 1;
+ }
return 0;
});
var mentions = {};
var index = 0;
for (var i = 0; i < users.length && index < MAX_ITEMS_IN_LIST; i++) {
- if (this.alreadyMentioned(users[i].username)) continue;
-
- if ((users[i].first_name && users[i].first_name.lastIndexOf(mentionText,0) === 0)
- || (users[i].last_name && users[i].last_name.lastIndexOf(mentionText,0) === 0) || users[i].username.lastIndexOf(mentionText,0) === 0) {
+ if ((users[i].first_name && users[i].first_name.lastIndexOf(mentionText, 0) === 0) ||
+ (users[i].last_name && users[i].last_name.lastIndexOf(mentionText, 0) === 0) ||
+ users[i].username.lastIndexOf(mentionText, 0) === 0) {
mentions[index] = (
<Mention
ref={'mention' + index}
@@ -198,7 +199,7 @@ module.exports = React.createClass({
secondary_text={Utils.getFullName(users[i])}
id={users[i].id}
listId={index}
- isFocused={this.state.selectedMention === index ? "mentions-focus" : ""}
+ isFocused={this.state.selectedMention === index ? 'mentions-focus' : ''}
handleMouseEnter={function(value) { return function() { self.handleMouseEnter(value); } }(index)}
handleClick={this.handleClick} />
);
@@ -208,21 +209,23 @@ module.exports = React.createClass({
var numMentions = Object.keys(mentions).length;
- if (numMentions < 1) return null;
+ if (numMentions < 1) {
+ return null;
+ }
- var $mention_tab = $('#'+this.props.id);
- var maxHeight = Math.min(MAX_HEIGHT_LIST, $mention_tab.offset().top - 10);
+ var $mentionTab = $('#' + this.props.id);
+ var maxHeight = Math.min(MAX_HEIGHT_LIST, $mentionTab.offset().top - 10);
var style = {
- height: Math.min(maxHeight, (numMentions*ITEM_HEIGHT) + 4),
- width: $mention_tab.parent().width(),
- bottom: $(window).height() - $mention_tab.offset().top,
- left: $mention_tab.offset().left
+ height: Math.min(maxHeight, (numMentions * ITEM_HEIGHT) + 4),
+ width: $mentionTab.parent().width(),
+ bottom: $(window).height() - $mentionTab.offset().top,
+ left: $mentionTab.offset().left
};
return (
- <div className="mentions--top" style={style}>
- <div ref="mentionlist" className="mentions-box" id="mentionsbox">
- { mentions }
+ <div className='mentions--top' style={style}>
+ <div ref='mentionlist' className='mentions-box' id='mentionsbox'>
+ {mentions}
</div>
</div>
);
diff --git a/web/react/components/new_channel.jsx b/web/react/components/new_channel.jsx
index 49e088458..c22147022 100644
--- a/web/react/components/new_channel.jsx
+++ b/web/react/components/new_channel.jsx
@@ -1,138 +1,151 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var asyncClient = require('../utils/async_client.jsx');
var UserStore = require('../stores/user_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
-var Constants = require('../utils/constants.jsx');
module.exports = React.createClass({
+ displayName: 'NewChannelModal',
handleSubmit: function(e) {
e.preventDefault();
var channel = {};
- var state = { server_error: "" };
+ var state = {serverError: ''};
channel.display_name = this.refs.display_name.getDOMNode().value.trim();
if (!channel.display_name) {
- state.display_name_error = "This field is required";
+ state.displayNameError = 'This field is required';
state.inValid = true;
- }
- else if (channel.display_name.length > 22) {
- state.display_name_error = "This field must be less than 22 characters";
+ } else if (channel.display_name.length > 22) {
+ state.displayNameError = 'This field must be less than 22 characters';
state.inValid = true;
- }
- else {
- state.display_name_error = "";
+ } else {
+ state.displayNameError = '';
}
channel.name = this.refs.channel_name.getDOMNode().value.trim();
if (!channel.name) {
- state.name_error = "This field is required";
+ state.nameError = 'This field is required';
state.inValid = true;
- }
- else if(channel.name.length > 22){
- state.name_error = "This field must be less than 22 characters";
+ } else if (channel.name.length > 22) {
+ state.nameError = 'This field must be less than 22 characters';
state.inValid = true;
- }
- else {
- var cleaned_name = utils.cleanUpUrlable(channel.name);
- if (cleaned_name != channel.name) {
- state.name_error = "Must be lowercase alphanumeric characters, allowing '-' but not starting or ending with '-'";
+ } else {
+ var cleanedName = utils.cleanUpUrlable(channel.name);
+ if (cleanedName !== channel.name) {
+ state.nameError = "Must be lowercase alphanumeric characters, allowing '-' but not starting or ending with '-'";
state.inValid = true;
- }
- else {
- state.name_error = "";
+ } else {
+ state.nameError = '';
}
}
this.setState(state);
- if (state.inValid)
+ if (state.inValid) {
return;
+ }
var cu = UserStore.getCurrentUser();
channel.team_id = cu.team_id;
channel.description = this.refs.channel_desc.getDOMNode().value.trim();
- channel.type = this.state.channel_type;
+ channel.type = this.state.channelType;
var self = this;
client.createChannel(channel,
function() {
- this.refs.display_name.getDOMNode().value = "";
- this.refs.channel_name.getDOMNode().value = "";
- this.refs.channel_desc.getDOMNode().value = "";
+ this.refs.display_name.getDOMNode().value = '';
+ this.refs.channel_name.getDOMNode().value = '';
+ this.refs.channel_desc.getDOMNode().value = '';
$(self.refs.modal.getDOMNode()).modal('hide');
- window.location = TeamStore.getCurrentTeamUrl() + "/channels/" + channel.name;
+ window.location = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name;
asyncClient.getChannels(true);
}.bind(this),
function(err) {
- state.server_error = err.message;
+ state.serverError = err.message;
state.inValid = true;
this.setState(state);
}.bind(this)
);
},
- displayNameKeyUp: function(e) {
- var display_name = this.refs.display_name.getDOMNode().value.trim();
- var channel_name = utils.cleanUpUrlable(display_name);
- this.refs.channel_name.getDOMNode().value = channel_name;
+ displayNameKeyUp: function() {
+ var displayName = this.refs.display_name.getDOMNode().value.trim();
+ var channelName = utils.cleanUpUrlable(displayName);
+ this.refs.channel_name.getDOMNode().value = channelName;
},
componentDidMount: function() {
var self = this;
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
var button = e.relatedTarget;
- self.setState({ channel_type: $(button).attr('data-channeltype') });
+ self.setState({channelType: $(button).attr('data-channeltype')});
});
},
getInitialState: function() {
- return { channel_type: "" };
+ return {channelType: ''};
},
render: function() {
+ var displayNameError = null;
+ var nameError = null;
+ var serverError = null;
+ var displayNameClass = 'form-group';
+ var nameClass = 'form-group';
- var display_name_error = this.state.display_name_error ? <label className='control-label'>{ this.state.display_name_error }</label> : null;
- var name_error = this.state.name_error ? <label className='control-label'>{ this.state.name_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;
+ if (this.state.displayNameError) {
+ displayNameError = <label className='control-label'>{this.state.displayNameError}</label>;
+ displayNameClass += ' has-error';
+ }
+ if (this.state.nameError) {
+ nameError = <label className='control-label'>{this.state.nameError}</label>;
+ nameClass += ' has-error';
+ }
+ if (this.state.serverError) {
+ serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
+ }
+
+ var channelTerm = 'Channel';
+ if (this.state.channelType === 'P') {
+ channelTerm = 'Group';
+ }
return (
- <div className="modal fade" id="new_channel" ref="modal" tabIndex="-1" role="dialog" aria-hidden="true">
- <div className="modal-dialog">
- <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>
+ <div className='modal fade' id='new_channel' ref='modal' 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'>
+ <span aria-hidden='true'>&times;</span>
+ <span className='sr-only'>Cancel</span>
</button>
- <h4 className="modal-title">New Channel</h4>
+ <h4 className='modal-title'>New {channelTerm}</h4>
</div>
- <div className="modal-body">
- <form role="form">
- <div className={ this.state.display_name_error ? "form-group has-error" : "form-group" }>
+ <form role='form'>
+ <div className='modal-body'>
+ <div className={displayNameClass}>
<label className='control-label'>Display Name</label>
- <input onKeyUp={this.displayNameKeyUp} type="text" ref="display_name" className="form-control" placeholder="Enter display name" maxLength="64" />
- { display_name_error }
+ <input onKeyUp={this.displayNameKeyUp} type='text' ref='display_name' className='form-control' placeholder='Enter display name' maxLength='64' />
+ {displayNameError}
</div>
- <div className={ this.state.name_error ? "form-group has-error" : "form-group" }>
+ <div className={nameClass}>
<label className='control-label'>Handle</label>
- <input type="text" className="form-control" ref="channel_name" placeholder="lowercase alphanumeric's only" maxLength="64" />
- { name_error }
+ <input type='text' className='form-control' ref='channel_name' placeholder="lowercase alphanumeric's only" maxLength='64' />
+ {nameError}
</div>
- <div className="form-group">
+ <div className='form-group'>
<label className='control-label'>Description</label>
- <textarea className="form-control no-resize" ref="channel_desc" rows="3" placeholder="Description" maxLength="1024"></textarea>
+ <textarea className='form-control no-resize' ref='channel_desc' rows='3' placeholder='Description' maxLength='1024'></textarea>
</div>
- { server_error }
- </form>
- </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">Create New Channel</button>
- </div>
+ {serverError}
+ </div>
+ <div className='modal-footer'>
+ <button type='button' className='btn btn-default' data-dismiss='modal'>Cancel</button>
+ <button onClick={this.handleSubmit} type='submit' className='btn btn-primary'>Create New {channelTerm}</button>
+ </div>
+ </form>
</div>
</div>
</div>
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
index 3f59d5843..bb1b1704c 100644
--- a/web/react/components/post_list.jsx
+++ b/web/react/components/post_list.jsx
@@ -37,15 +37,23 @@ module.exports = React.createClass({
componentDidMount: function() {
var user = UserStore.getCurrentUser();
if (user.props && user.props.theme) {
- utils.changeCss('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;');
utils.changeCss('div.theme', 'background-color:'+user.props.theme+';');
utils.changeCss('.btn.btn-primary', 'background: ' + user.props.theme+';');
- utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, -10) +';');
utils.changeCss('.modal .modal-header', 'background: ' + user.props.theme+';');
utils.changeCss('.mention', 'background: ' + user.props.theme+';');
utils.changeCss('.mention-link', 'color: ' + user.props.theme+';');
utils.changeCss('@media(max-width: 768px){.search-bar__container', 'background: ' + user.props.theme+';}');
}
+ if (user.props.theme != '#000000' && user.props.theme != '#585858') {
+ utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, -10) +';');
+ utils.changeCss('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;');
+ } else if (user.props.theme == '#000000') {
+ utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, +50) +';');
+ $('.team__header').addClass('theme--black');
+ } else if (user.props.theme == '#585858') {
+ utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, +10) +';');
+ $('.team__header').addClass('theme--gray');
+ }
PostStore.addChangeListener(this._onChange);
ChannelStore.addChangeListener(this._onChange);
@@ -311,7 +319,6 @@ module.exports = React.createClass({
} else if (channel.type === 'D') {
var teammate = utils.getDirectTeammate(channel.id)
-
if (teammate) {
var teammate_name = teammate.nickname.length > 0 ? teammate.nickname : teammate.username;
more_messages = (
@@ -399,7 +406,7 @@ module.exports = React.createClass({
var postCtls = [];
if (posts) {
- var previousPostDay = posts[order[order.length-1]] ? utils.getDateForUnixTicks(posts[order[order.length-1]].create_at): new Date();
+ var previousPostDay = new Date(0);
var currentPostDay;
for (var i = order.length-1; i >= 0; i--) {
diff --git a/web/react/components/setting_picture.jsx b/web/react/components/setting_picture.jsx
index fa4c8bb62..e97b67706 100644
--- a/web/react/components/setting_picture.jsx
+++ b/web/react/components/setting_picture.jsx
@@ -20,8 +20,14 @@ module.exports = React.createClass({
}
},
render: function() {
- var client_error = this.props.client_error ? <div className='form-group has-error'><label className='control-label'>{ this.props.client_error }</label></div> : null;
- var server_error = this.props.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.props.server_error }</label></div> : null;
+ var clientError = null;
+ if (this.props.client_error) {
+ clientError = <div className='form-group has-error'><label className='control-label'>{this.props.client_error}</label></div>;
+ }
+ var serverError = null;
+ if (this.props.server_error) {
+ serverError = <div className='form-group has-error'><label className='control-label'>{this.props.server_error}</label></div>;
+ }
var img = null;
if (this.props.picture) {
@@ -30,8 +36,20 @@ module.exports = React.createClass({
img = (<img ref='image' className='profile-img' src={this.props.src}/>);
}
- var self = this;
+ var confirmButton;
+ if (this.props.loadingPicture) {
+ confirmButton = <img className='spinner' src='/static/images/load.gif'/>;
+ } else {
+ var confirmButtonClass = 'btn btn-sm';
+ if (this.props.submitActive) {
+ confirmButtonClass += ' btn-primary';
+ } else {
+ confirmButtonClass += ' btn-inactive disabled';
+ }
+ confirmButton = <a className={confirmButtonClass} onClick={this.props.submit}>Save</a>;
+ }
+ var self = this;
return (
<ul className='section-max'>
<li className='col-xs-12 section-title'>{this.props.title}</li>
@@ -41,10 +59,10 @@ module.exports = React.createClass({
{img}
</li>
<li className='setting-list-item'>
- {server_error}
- {client_error}
+ {serverError}
+ {clientError}
<span className='btn btn-sm btn-primary btn-file sel-btn'>Select<input ref='input' accept='.jpg,.png,.bmp' type='file' onChange={this.props.pictureChange}/></span>
- <a className={this.props.submitActive ? 'btn btn-sm btn-primary' : 'btn btn-sm btn-inactive disabled'} onClick={this.props.submit}>Save</a>
+ {confirmButton}
<a className='btn btn-sm theme' href='#' onClick={self.props.updateSection}>Cancel</a>
</li>
</ul>
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index 5b8d6c542..1d39f5f67 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -7,7 +7,7 @@ var AsyncClient = require('../utils/async_client.jsx');
var SocketStore = require('../stores/socket_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
-var BrowserStore = require('../stores/browser_store.jsx')
+var BrowserStore = require('../stores/browser_store.jsx');
var utils = require('../utils/utils.jsx');
var SidebarHeader = require('./sidebar_header.jsx');
var SearchBox = require('./search_bar.jsx');
@@ -17,13 +17,15 @@ var ActionTypes = Constants.ActionTypes;
function getStateFromStores() {
var members = ChannelStore.getAllMembers();
- var team_member_map = UserStore.getActiveOnlyProfiles();
- var current_id = ChannelStore.getCurrentId();
+ var teamMemberMap = UserStore.getActiveOnlyProfiles();
+ var currentId = ChannelStore.getCurrentId();
var teammates = [];
- for (var id in team_member_map) {
- if (id === UserStore.getCurrentId()) continue;
- teammates.push(team_member_map[id]);
+ for (var id in teamMemberMap) {
+ if (id === UserStore.getCurrentId()) {
+ continue;
+ }
+ teammates.push(teamMemberMap[id]);
}
// Create lists of all read and unread direct channels
@@ -32,11 +34,11 @@ function getStateFromStores() {
for (var i = 0; i < teammates.length; i++) {
var teammate = teammates[i];
- if (teammate.id == UserStore.getCurrentId()) {
+ if (teammate.id === UserStore.getCurrentId()) {
continue;
}
- var channelName = "";
+ var channelName = '';
if (teammate.id > UserStore.getCurrentId()) {
channelName = UserStore.getCurrentId() + '__' + teammate.id;
} else {
@@ -46,17 +48,17 @@ function getStateFromStores() {
var channel = ChannelStore.getByName(channelName);
if (channel != null) {
- channel.display_name = utils.getDisplayName(teammate);
+ channel.display_name = teammate.username;
channel.teammate_username = teammate.username;
channel.status = UserStore.getStatus(teammate.id);
var channelMember = members[channel.id];
- var msg_count = channel.total_msg_count - channelMember.msg_count;
- if (msg_count > 0) {
- channel.unread = msg_count;
+ var msgCount = channel.total_msg_count - channelMember.msg_count;
+ if (msgCount > 0) {
+ channel.unread = msgCount;
showDirectChannels.push(channel);
- } else if (current_id === channel.id) {
+ } else if (currentId === channel.id) {
showDirectChannels.push(channel);
} else {
readDirectChannels.push(channel);
@@ -74,13 +76,22 @@ function getStateFromStores() {
// If we don't have MAX_DMS unread channels, sort the read list by last_post_at
if (showDirectChannels.length < Constants.MAX_DMS) {
- readDirectChannels.sort(function(a,b) {
+ readDirectChannels.sort(function(a, b) {
// sort by last_post_at first
- if (a.last_post_at > b.last_post_at) return -1;
- if (a.last_post_at < b.last_post_at) return 1;
+ if (a.last_post_at > b.last_post_at) {
+ return -1;
+ }
+ if (a.last_post_at < b.last_post_at) {
+ return 1;
+ }
+
// if last_post_at is equal, sort by name
- if (a.display_name < b.display_name) return -1;
- if (a.display_name > b.display_name) return 1;
+ if (a.display_name < b.display_name) {
+ return -1;
+ }
+ if (a.display_name > b.display_name) {
+ return 1;
+ }
return 0;
});
@@ -91,15 +102,19 @@ function getStateFromStores() {
}
readDirectChannels = readDirectChannels.slice(index);
- showDirectChannels.sort(function(a,b) {
- if (a.display_name < b.display_name) return -1;
- if (a.display_name > b.display_name) return 1;
+ showDirectChannels.sort(function(a, b) {
+ if (a.display_name < b.display_name) {
+ return -1;
+ }
+ if (a.display_name > b.display_name) {
+ return 1;
+ }
return 0;
});
}
return {
- active_id: current_id,
+ active_id: currentId,
channels: ChannelStore.getAll(),
members: members,
showDirectChannels: showDirectChannels,
@@ -108,12 +123,13 @@ function getStateFromStores() {
}
module.exports = React.createClass({
+ displayName: 'Sidebar',
componentDidMount: function() {
- ChannelStore.addChangeListener(this._onChange);
- UserStore.addChangeListener(this._onChange);
- UserStore.addStatusesChangeListener(this._onChange);
- SocketStore.addChangeListener(this._onSocketChange);
- $(".nav-pills__container").perfectScrollbar();
+ ChannelStore.addChangeListener(this.onChange);
+ UserStore.addChangeListener(this.onChange);
+ UserStore.addStatusesChangeListener(this.onChange);
+ SocketStore.addChangeListener(this.onSocketChange);
+ $('.nav-pills__container').perfectScrollbar();
this.updateTitle();
},
@@ -121,93 +137,88 @@ module.exports = React.createClass({
this.updateTitle();
},
componentWillUnmount: function() {
- ChannelStore.removeChangeListener(this._onChange);
- UserStore.removeChangeListener(this._onChange);
- UserStore.removeStatusesChangeListener(this._onChange);
- SocketStore.removeChangeListener(this._onSocketChange);
+ ChannelStore.removeChangeListener(this.onChange);
+ UserStore.removeChangeListener(this.onChange);
+ UserStore.removeStatusesChangeListener(this.onChange);
+ SocketStore.removeChangeListener(this.onSocketChange);
},
- _onChange: function() {
+ onChange: function() {
var newState = getStateFromStores();
if (!utils.areStatesEqual(newState, this.state)) {
this.setState(newState);
}
},
- _onSocketChange: function(msg) {
- if (msg.action == "posted") {
+ onSocketChange: function(msg) {
+ if (msg.action === 'posted') {
if (ChannelStore.getCurrentId() === msg.channel_id) {
AsyncClient.getChannels(true, window.isActive);
} else {
AsyncClient.getChannels(true);
}
- if (UserStore.getCurrentId() != msg.user_id) {
-
+ if (UserStore.getCurrentId() !== msg.user_id) {
var mentions = msg.props.mentions ? JSON.parse(msg.props.mentions) : [];
var channel = ChannelStore.get(msg.channel_id);
var user = UserStore.getCurrentUser();
- if (user.notify_props && ((user.notify_props.desktop === "mention" && mentions.indexOf(user.id) === -1 && channel.type !== 'D') || user.notify_props.desktop === "none")) {
+ if (user.notify_props && ((user.notify_props.desktop === 'mention' && mentions.indexOf(user.id) === -1 && channel.type !== 'D') || user.notify_props.desktop === 'none')) {
return;
}
var member = ChannelStore.getMember(msg.channel_id);
- if ((member.notify_level === "mention" && mentions.indexOf(user.id) === -1) || member.notify_level === "none" || member.notify_level === "quiet") {
+ if ((member.notify_level === 'mention' && mentions.indexOf(user.id) === -1) || member.notify_level === 'none' || member.notify_level === 'quiet') {
return;
}
- var username = "Someone";
+ var username = 'Someone';
if (UserStore.hasProfile(msg.user_id)) {
username = UserStore.getProfile(msg.user_id).username;
}
- var title = channel ? channel.display_name : "Posted";
+ var title = channel ? channel.display_name : 'Posted';
- var repRegex = new RegExp("<br>", "g");
+ var repRegex = new RegExp('<br>', 'g');
var post = JSON.parse(msg.props.post);
var msgProps = msg.props;
- var msg = post.message.replace(repRegex, "\n").replace(/\n+/g, " ").replace("<mention>", "").replace("</mention>", "");
-
- if (msg.length > 50) {
- msg = msg.substring(0,49) + "...";
+ var notifyText = post.message.replace(repRegex, '\n').replace(/\n+/g, ' ').replace('<mention>', '').replace('</mention>', '');
+
+ if (notifyText.length > 50) {
+ notifyText = notifyText.substring(0, 49) + '...';
}
- if (msg.length === 0) {
+ if (notifyText.length === 0) {
if (msgProps.image) {
- utils.notifyMe(title, username + " uploaded an image", channel);
- }
- else if (msgProps.otherFile) {
- utils.notifyMe(title, username + " uploaded a file", channel);
+ utils.notifyMe(title, username + ' uploaded an image', channel);
+ } else if (msgProps.otherFile) {
+ utils.notifyMe(title, username + ' uploaded a file', channel);
+ } else {
+ utils.notifyMe(title, username + ' did something new', channel);
}
- else {
- utils.notifyMe(title, username + " did something new", channel);
- }
- }
- else {
- utils.notifyMe(title, username + " wrote: " + msg, channel);
+ } else {
+ utils.notifyMe(title, username + ' wrote: ' + notifyText, channel);
}
- if (!user.notify_props || user.notify_props.desktop_sound === "true") {
+ if (!user.notify_props || user.notify_props.desktop_sound === 'true') {
utils.ding();
}
}
-
- } else if (msg.action == "viewed") {
+ } else if (msg.action === 'viewed') {
if (ChannelStore.getCurrentId() != msg.channel_id) {
AsyncClient.getChannels(true);
}
- } else if (msg.action == "user_added") {
+ } else if (msg.action === 'user_added') {
if (UserStore.getCurrentId() === msg.user_id) {
AsyncClient.getChannels(true);
}
- } else if(msg.action === "user_removed") {
- if(msg.user_id === UserStore.getCurrentId()) {
+ } else if (msg.action === 'user_removed') {
+ if (msg.user_id === UserStore.getCurrentId()) {
AsyncClient.getChannels(true);
- if(msg.props.channel_id === ChannelStore.getCurrentId() && $('#removed_from_channel').length > 0) {
+ if (msg.props.channel_id === ChannelStore.getCurrentId() && $('#removed_from_channel').length > 0) {
var sentState = {};
sentState.channelName = ChannelStore.getCurrent().display_name;
sentState.remover = UserStore.getProfile(msg.props.remover).username;
- BrowserStore.setItem('channel-removed-state',sentState);
+ BrowserStore.setItem('channel-removed-state', sentState);
$('#removed_from_channel').modal('show');
}
}
@@ -217,10 +228,10 @@ module.exports = React.createClass({
var channel = ChannelStore.getCurrent();
if (channel) {
if (channel.type === 'D') {
- var teammate_username = utils.getDirectTeammate(channel.id).username
- document.title = teammate_username + " " + document.title.substring(document.title.lastIndexOf("-"));
+ var teammate_username = utils.getDirectTeammate(channel.id).username;
+ document.title = teammate_username + ' ' + document.title.substring(document.title.lastIndexOf('-'));
} else {
- document.title = channel.display_name + " " + document.title.substring(document.title.lastIndexOf("-"))
+ document.title = channel.display_name + ' ' + document.title.substring(document.title.lastIndexOf('-'));
}
}
},
@@ -229,92 +240,96 @@ module.exports = React.createClass({
},
render: function() {
var members = this.state.members;
- var newsActive = window.location.pathname === "/" ? "active" : "";
+ var newsActive = window.location.pathname === '/' ? 'active' : '';
var badgesActive = false;
var self = this;
var channelItems = this.state.channels.map(function(channel) {
if (channel.type != 'O') {
- return "";
+ return '';
}
var channelMember = members[channel.id];
- var active = channel.id === self.state.active_id ? "active" : "";
+ var active = channel.id === self.state.active_id ? 'active' : '';
- var msg_count = channel.total_msg_count - channelMember.msg_count;
- var titleClass = ""
- if (msg_count > 0 && channelMember.notify_level !== "quiet") {
- titleClass = "unread-title"
+ var msgCount = channel.total_msg_count - channelMember.msg_count;
+ var titleClass = '';
+ if (msgCount > 0 && channelMember.notify_level !== 'quiet') {
+ titleClass = 'unread-title';
}
- var badge = "";
+ var badge = '';
if (channelMember.mention_count > 0) {
- badge = <span className="badge pull-right small">{channelMember.mention_count}</span>;
+ badge = <span className='badge pull-right small'>{channelMember.mention_count}</span>;
badgesActive = true;
- titleClass = "unread-title"
+ titleClass = 'unread-title';
}
return (
- <li key={channel.id} className={active}><a className={"sidebar-channel " + titleClass} href="#" onClick={function(e){e.preventDefault(); utils.switchChannel(channel);}}>{badge}{channel.display_name}</a></li>
+ <li key={channel.id} className={active}><a className={'sidebar-channel ' + titleClass} href='#' onClick={function(e){e.preventDefault(); utils.switchChannel(channel);}}>{badge}{channel.display_name}</a></li>
);
});
var privateChannelItems = this.state.channels.map(function(channel) {
- if (channel.type != 'P') {
- return "";
+ if (channel.type !== 'P') {
+ return '';
}
var channelMember = members[channel.id];
- var active = channel.id === self.state.active_id ? "active" : "";
+ var active = channel.id === self.state.active_id ? 'active' : '';
- var msg_count = channel.total_msg_count - channelMember.msg_count;
- var titleClass = ""
- if (msg_count > 0 && channelMember.notify_level !== "quiet") {
- titleClass = "unread-title"
+ var msgCount = channel.total_msg_count - channelMember.msg_count;
+ var titleClass = ''
+ if (msgCount > 0 && channelMember.notify_level !== 'quiet') {
+ titleClass = 'unread-title'
}
- var badge = "";
+ var badge = '';
if (channelMember.mention_count > 0) {
- badge = <span className="badge pull-right small">{channelMember.mention_count}</span>;
+ badge = <span className='badge pull-right small'>{channelMember.mention_count}</span>;
badgesActive = true;
- titleClass = "unread-title"
+ titleClass = 'unread-title';
}
return (
- <li key={channel.id} className={active}><a className={"sidebar-channel " + titleClass} href="#" onClick={function(e){e.preventDefault(); utils.switchChannel(channel);}}>{badge}{channel.display_name}</a></li>
+ <li key={channel.id} className={active}><a className={'sidebar-channel ' + titleClass} href='#' onClick={function(e){e.preventDefault(); utils.switchChannel(channel);}}>{badge}{channel.display_name}</a></li>
);
});
var directMessageItems = this.state.showDirectChannels.map(function(channel) {
- var badge = "";
- var titleClass = "";
+ var badge = '';
+ var titleClass = '';
- var statusIcon = "";
- if (channel.status === "online") {
+ var statusIcon = '';
+ if (channel.status === 'online') {
statusIcon = Constants.ONLINE_ICON_SVG;
- } else if (channel.status === "away") {
+ } else if (channel.status === 'away') {
statusIcon = Constants.ONLINE_ICON_SVG;
} else {
statusIcon = Constants.OFFLINE_ICON_SVG;
}
if (!channel.fake) {
- var active = channel.id === self.state.active_id ? "active" : "";
+ var active = channel.id === self.state.active_id ? 'active' : '';
if (channel.unread) {
- badge = <span className="badge pull-right small">{channel.unread}</span>;
+ badge = <span className='badge pull-right small'>{channel.unread}</span>;
badgesActive = true;
- titleClass = "unread-title"
+ titleClass = 'unread-title';
+ }
+
+ function handleClick(e) {
+ e.preventDefault();
+ utils.switchChannel(channel, channel.teammate_username);
}
return (
- <li key={channel.name} className={active}><a className={"sidebar-channel " + titleClass} href="#" onClick={function(e){e.preventDefault(); utils.switchChannel(channel, channel.teammate_username);}}><span className="status" dangerouslySetInnerHTML={{__html: statusIcon}} /> {badge}{channel.display_name}</a></li>
+ <li key={channel.name} className={active}><a className={'sidebar-channel ' + titleClass} href='#' onClick={handleClick}><span className='status' dangerouslySetInnerHTML={{__html: statusIcon}} /> {badge}{channel.display_name}</a></li>
);
} else {
return (
- <li key={channel.name} className={active}><a className={"sidebar-channel " + titleClass} href={TeamStore.getCurrentTeamUrl() + "/channels/"+channel.name}><span className="status" dangerouslySetInnerHTML={{__html: statusIcon}} /> {badge}{channel.display_name}</a></li>
+ <li key={channel.name} className={active}><a className={'sidebar-channel ' + titleClass} href={TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name}><span className='status' dangerouslySetInnerHTML={{__html: statusIcon}} /> {badge}{channel.display_name}</a></li>
);
}
-
});
var link = document.createElement('link');
@@ -345,23 +360,23 @@ module.exports = React.createClass({
<SidebarHeader teamDisplayName={this.props.teamDisplayName} teamType={this.props.teamType} />
<SearchBox />
- <div className="nav-pills__container">
- <ul className="nav nav-pills nav-stacked">
- <li><h4>Channels<a className="add-channel-btn" href="#" data-toggle="modal" data-target="#new_channel" data-channeltype="O">+</a></h4></li>
+ <div className='nav-pills__container'>
+ <ul className='nav nav-pills nav-stacked'>
+ <li><h4>Channels<a className='add-channel-btn' href='#' data-toggle='modal' data-target='#new_channel' data-channeltype='O'>+</a></h4></li>
{channelItems}
- <li><a href="#" data-toggle="modal" className="nav-more" data-target="#more_channels" data-channeltype="O">More...</a></li>
+ <li><a href='#' data-toggle='modal' className='nav-more' data-target='#more_channels' data-channeltype='O'>More...</a></li>
</ul>
- <ul className="nav nav-pills nav-stacked">
- <li><h4>Private Groups<a className="add-channel-btn" href="#" data-toggle="modal" data-target="#new_channel" data-channeltype="P">+</a></h4></li>
+ <ul className='nav nav-pills nav-stacked'>
+ <li><h4>Private Groups<a className='add-channel-btn' href='#' data-toggle='modal' data-target='#new_channel' data-channeltype='P'>+</a></h4></li>
{privateChannelItems}
</ul>
- <ul className="nav nav-pills nav-stacked">
+ <ul className='nav nav-pills nav-stacked'>
<li><h4>Private Messages</h4></li>
{directMessageItems}
{ this.state.hideDirectChannels.length > 0 ?
- <li><a href="#" data-toggle="modal" className="nav-more" data-target="#more_direct_channels" data-channels={JSON.stringify(this.state.hideDirectChannels)}>{"More ("+this.state.hideDirectChannels.length+")"}</a></li>
- : "" }
+ <li><a href='#' data-toggle='modal' className='nav-more' data-target='#more_direct_channels' data-channels={JSON.stringify(this.state.hideDirectChannels)}>{'More ('+this.state.hideDirectChannels.length+')'}</a></li>
+ : '' }
</ul>
</div>
</div>
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index e01ddcd05..72b8547e5 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -1,15 +1,15 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
var Constants = require('../utils/constants.jsx');
function getStateFromStores() {
- return { teams: UserStore.getTeams() };
+ return {teams: UserStore.getTeams(), currentTeam: TeamStore.getCurrent()};
}
var NavbarDropdown = React.createClass({
@@ -19,20 +19,24 @@ var NavbarDropdown = React.createClass({
},
blockToggle: false,
componentDidMount: function() {
- UserStore.addTeamsChangeListener(this._onChange);
+ UserStore.addTeamsChangeListener(this.onListenerChange);
+ TeamStore.addChangeListener(this.onListenerChange);
var self = this;
- $(this.refs.dropdown.getDOMNode()).on('hide.bs.dropdown', function(e) {
+ $(this.refs.dropdown.getDOMNode()).on('hide.bs.dropdown', function() {
self.blockToggle = true;
- setTimeout(function(){self.blockToggle = false;}, 100);
+ setTimeout(function() {
+ self.blockToggle = false;
+ }, 100);
});
},
componentWillUnmount: function() {
- UserStore.removeTeamsChangeListener(this._onChange);
+ UserStore.removeTeamsChangeListener(this.onListenerChange);
+ TeamStore.removeChangeListener(this.onListenerChange);
$(this.refs.dropdown.getDOMNode()).off('hide.bs.dropdown');
},
- _onChange: function() {
+ onListenerChange: function() {
if (this.isMounted()) {
var newState = getStateFromStores();
if (!utils.areStatesEqual(newState, this.state)) {
@@ -44,62 +48,65 @@ var NavbarDropdown = React.createClass({
return getStateFromStores();
},
render: function() {
- var team_link = "";
- var invite_link = "";
- var manage_link = "";
- var rename_link = "";
+ var teamLink = '';
+ var inviteLink = '';
+ var manageLink = '';
+ var renameLink = '';
var currentUser = UserStore.getCurrentUser();
var isAdmin = false;
+ var teamSettings = null;
if (currentUser != null) {
- isAdmin = currentUser.roles.indexOf("admin") > -1;
+ isAdmin = currentUser.roles.indexOf('admin') > -1;
- invite_link = ( <li> <a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a> </li>);
+ inviteLink = (<li> <a href='#' data-toggle='modal' data-target='#invite_member'>Invite New Member</a> </li>);
- if (this.props.teamType == "O") {
- team_link = (
+ if (this.props.teamType === 'O') {
+ teamLink = (
<li>
- <a href="#" data-toggle="modal" data-target="#get_link" data-title="Team Invite" data-value={location.origin+"/signup_user_complete/?id="+currentUser.team_id}>Get Team Invite Link</a>
+ <a href='#' data-toggle='modal' data-target='#get_link' data-title='Team Invite' data-value={utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + currentUser.team_id}>Get Team Invite Link</a>
</li>
);
}
}
if (isAdmin) {
- manage_link = ( <li> <a href="#" data-toggle="modal" data-target="#team_members">Manage Team</a> </li>);
- rename_link = ( <li> <a href="#" data-toggle="modal" data-target="#rename_team_link">Rename</a> </li>);
+ manageLink = (<li> <a href='#' data-toggle='modal' data-target='#team_members'>Manage Team</a> </li>);
+ renameLink = (<li> <a href='#' data-toggle='modal' data-target='#rename_team_link'>Rename</a> </li>);
+ teamSettings = (<li> <a href='#' data-toggle='modal' data-target='#team_settings'>Team Settings</a> </li>);
}
var teams = [];
- teams.push(<li className="divider" key="div"></li>);
- if (this.state.teams.length > 1) {
- for (var i = 0; i < this.state.teams.length; i++) {
- var teamName = this.state.teams[i];
-
- teams.push(<li key={ teamName }><a href={utils.getWindowLocationOrigin() + "/" + teamName }>Switch to { teamName }</a></li>);
- }
+ teams.push(<li className='divider' key='div'></li>);
+ if (this.state.teams.length > 1 && this.state.currentTeam) {
+ var curTeamName = this.state.currentTeam.name;
+ this.state.teams.forEach(function(teamName) {
+ if (teamName !== curTeamName) {
+ teams.push(<li key={teamName}><a href={utils.getWindowLocationOrigin() + '/' + teamName}>Switch to {teamName}</a></li>);
+ }
+ });
}
- teams.push(<li><a href={utils.getWindowLocationOrigin() + "/signup_team" }>Create a New Team</a></li>);
+ teams.push(<li><a href={utils.getWindowLocationOrigin() + '/signup_team'}>Create a New Team</a></li>);
return (
- <ul className="nav navbar-nav navbar-right">
- <li ref="dropdown" className="dropdown">
- <a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
- <span className="dropdown__icon" dangerouslySetInnerHTML={{__html: Constants.MENU_ICON }} />
+ <ul className='nav navbar-nav navbar-right'>
+ <li ref='dropdown' className='dropdown'>
+ <a href='#' className='dropdown-toggle' data-toggle='dropdown' role='button' aria-expanded='false'>
+ <span className='dropdown__icon' dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} />
</a>
- <ul className="dropdown-menu" role="menu">
- <li><a href="#" data-toggle="modal" data-target="#user_settings1">Account Settings</a></li>
- { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : null }
- { invite_link }
- { team_link }
- { manage_link }
- { rename_link }
- <li><a href="#" onClick={this.handleLogoutClick}>Logout</a></li>
- { teams }
- <li className="divider"></li>
- <li><a target="_blank" href={config.HelpLink}>Help</a></li>
- <li><a target="_blank" href={config.ReportProblemLink}>Report a Problem</a></li>
+ <ul className='dropdown-menu' role='menu'>
+ <li><a href='#' data-toggle='modal' data-target='#user_settings1'>Account Settings</a></li>
+ {teamSettings}
+ {inviteLink}
+ {teamLink}
+ {manageLink}
+ {renameLink}
+ <li><a href='#' onClick={this.handleLogoutClick}>Logout</a></li>
+ {teams}
+ <li className='divider'></li>
+ <li><a target='_blank' href={config.HelpLink}>Help</a></li>
+ <li><a target='_blank' href={config.ReportProblemLink}>Report a Problem</a></li>
</ul>
</li>
</ul>
@@ -109,14 +116,13 @@ var NavbarDropdown = React.createClass({
module.exports = React.createClass({
displayName: 'SidebarHeader',
-
getDefaultProps: function() {
return {
teamDisplayName: config.SiteName
};
},
- toggleDropdown: function(e) {
+ toggleDropdown: function() {
if (this.refs.dropdown.blockToggle) {
this.refs.dropdown.blockToggle = false;
return;
@@ -126,25 +132,26 @@ module.exports = React.createClass({
render: function() {
var me = UserStore.getCurrentUser();
+ var profilePicture = null;
if (!me) {
return null;
}
+ if (me.last_picture_update) {
+ profilePicture = (<img className='user__picture' src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at} />);
+ }
+
return (
- <div className="team__header theme">
- <a href="#" onClick={this.toggleDropdown}>
- { me.last_picture_update ?
- <img className="user__picture" src={"/api/v1/users/" + me.id + "/image?time=" + me.update_at} />
- :
- null
- }
- <div className="header__info">
- <div className="user__name">{ '@' + me.username}</div>
- <div className="team__name">{ this.props.teamDisplayName }</div>
+ <div className='team__header theme'>
+ <a href='#' onClick={this.toggleDropdown}>
+ {profilePicture}
+ <div className='header__info'>
+ <div className='user__name'>{'@' + me.username}</div>
+ <div className='team__name'>{this.props.teamDisplayName }</div>
</div>
</a>
- <NavbarDropdown ref="dropdown" teamType={this.props.teamType} />
+ <NavbarDropdown ref='dropdown' teamType={this.props.teamType} />
</div>
);
}
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index 15306a499..2439719a1 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -3,6 +3,7 @@
var UserStore = require('../stores/user_store.jsx');
var client = require('../utils/client.jsx');
+var utils = require('../utils/utils.jsx');
module.exports = React.createClass({
handleLogoutClick: function(e) {
@@ -10,65 +11,77 @@ module.exports = React.createClass({
client.logout();
},
render: function() {
- var team_link = "";
- var invite_link = "";
- var manage_link = "";
- var rename_link = "";
- var currentUser = UserStore.getCurrentUser()
+ var teamLink = '';
+ var inviteLink = '';
+ var teamSettingsLink = '';
+ var manageLink = '';
+ var renameLink = '';
+ var currentUser = UserStore.getCurrentUser();
var isAdmin = false;
if (currentUser != null) {
- isAdmin = currentUser.roles.indexOf("admin") > -1;
+ isAdmin = currentUser.roles.indexOf('admin') > -1;
- invite_link = (
+ inviteLink = (
<li>
- <a href="#" data-toggle="modal" data-target="#invite_member"><i className="glyphicon glyphicon-user"></i>Invite New Member</a>
+ <a href='#' data-toggle='modal' data-target='#invite_member'><i className='glyphicon glyphicon-user'></i>Invite New Member</a>
</li>
);
- if (this.props.teamType == "O") {
- team_link = (
+ if (this.props.teamType === 'O') {
+ teamLink = (
<li>
- <a href="#" data-toggle="modal" data-target="#get_link" data-title="Team Invite" data-value={location.origin+"/signup_user_complete/?id="+currentUser.team_id}><i className="glyphicon glyphicon-link"></i>Get Team Invite Link</a>
+ <a href='#' data-toggle='modal' data-target='#get_link' data-title='Team Invite' data-value={utils.getWindowLocationOrigin()+'/signup_user_complete/?id='+currentUser.team_id}><i className='glyphicon glyphicon-link'></i>Get Team Invite Link</a>
</li>
);
}
}
if (isAdmin) {
- manage_link = (
+ teamSettingsLink = (
<li>
- <a href="#" data-toggle="modal" data-target="#team_members"><i className="glyphicon glyphicon-wrench"></i>Manage Team</a>
+ <a href='#' data-toggle='modal' data-target='#team_settings'><i className='glyphicon glyphicon-globe'></i>Team Settings</a>
</li>
);
- rename_link = (
+ manageLink = (
<li>
- <a href="#" data-toggle="modal" data-target="#rename_team_link"><i className="glyphicon glyphicon-pencil"></i>Rename</a>
+ <a href='#' data-toggle='modal' data-target='#team_members'><i className='glyphicon glyphicon-wrench'></i>Manage Team</a>
+ </li>
+ );
+ renameLink = (
+ <li>
+ <a href='#' data-toggle='modal' data-target='#rename_team_link'><i className='glyphicon glyphicon-pencil'></i>Rename</a>
</li>
);
}
- var siteName = config.SiteName != null ? config.SiteName : "";
- var teamDisplayName = this.props.teamDisplayName ? this.props.teamDisplayName : siteName;
+ var siteName = '';
+ if (config.SiteName != null) {
+ siteName = config.SiteName;
+ }
+ var teamDisplayName = siteName;
+ if (this.props.teamDisplayName) {
+ teamDisplayName = this.props.teamDisplayName;
+ }
return (
<div>
- <div className="team__header theme">
- <a className="team__name" href="/channels/town-square">{ teamDisplayName }</a>
+ <div className='team__header theme'>
+ <a className='team__name' href='/channels/town-square'>{teamDisplayName}</a>
</div>
- <div className="nav-pills__container">
- <ul className="nav nav-pills nav-stacked">
- <li><a href="#" data-toggle="modal" data-target="#user_settings1"><i className="glyphicon glyphicon-cog"></i>Account Settings</a></li>
- { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings"><i className="glyphicon glyphicon-globe"></i>Team Settings</a></li> : "" }
- { invite_link }
- { team_link }
- { manage_link }
- { rename_link }
- <li><a href="#" onClick={this.handleLogoutClick}><i className="glyphicon glyphicon-log-out"></i>Logout</a></li>
- <li className="divider"></li>
- <li><a target="_blank" href="/static/help/configure_links.html"><i className="glyphicon glyphicon-question-sign"></i>Help</a></li>
- <li><a target="_blank" href="/static/help/configure_links.html"><i className="glyphicon glyphicon-earphone"></i>Report a Problem</a></li>
+ <div className='nav-pills__container'>
+ <ul className='nav nav-pills nav-stacked'>
+ <li><a href='#' data-toggle='modal' data-target='#user_settings1'><i className='glyphicon glyphicon-cog'></i>Account Settings</a></li>
+ {teamSettingsLink}
+ {inviteLink}
+ {teamLink}
+ {manageLink}
+ {renameLink}
+ <li><a href='#' onClick={this.handleLogoutClick}><i className='glyphicon glyphicon-log-out'></i>Logout</a></li>
+ <li className='divider'></li>
+ <li><a target='_blank' href='/static/help/configure_links.html'><i className='glyphicon glyphicon-question-sign'></i>Help</a></li>
+ <li><a target='_blank' href='/static/help/configure_links.html'><i className='glyphicon glyphicon-earphone'></i>Report a Problem</a></li>
</ul>
</div>
</div>
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index 03808e821..b21553d8a 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -1,7 +1,6 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
@@ -9,36 +8,41 @@ var BrowserStore = require('../stores/browser_store.jsx');
module.exports = React.createClass({
handleSubmit: function(e) {
- e.preventDefault();
+ e.preventDefault();
this.state.user.username = this.refs.name.getDOMNode().value.trim();
if (!this.state.user.username) {
- this.setState({name_error: "This field is required", email_error: "", password_error: "", server_error: ""});
+ this.setState({nameError: 'This field is required', emailError: '', passwordError: '', serverError: ''});
return;
}
- var username_error = utils.isValidUsername(this.state.user.username);
- if (username_error === "Cannot use a reserved word as a username.") {
- this.setState({name_error: "This username is reserved, please choose a new one.", email_error: "", password_error: "", server_error: ""});
+ var usernameError = utils.isValidUsername(this.state.user.username);
+ if (usernameError === 'Cannot use a reserved word as a username.') {
+ this.setState({nameError: 'This username is reserved, please choose a new one.', emailError: '', passwordError: '', serverError: ''});
return;
- } else if (username_error) {
- this.setState({name_error: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'.", email_error: "", password_error: "", server_error: ""});
+ } else if (usernameError) {
+ this.setState({
+ nameError: 'Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\' and \'_\'.',
+ emailError: '',
+ passwordError: '',
+ serverError: ''
+ });
return;
}
this.state.user.email = this.refs.email.getDOMNode().value.trim();
if (!this.state.user.email) {
- this.setState({name_error: "", email_error: "This field is required", password_error: ""});
+ this.setState({nameError: '', emailError: 'This field is required', passwordError: ''});
return;
}
this.state.user.password = this.refs.password.getDOMNode().value.trim();
if (!this.state.user.password || this.state.user.password .length < 5) {
- this.setState({name_error: "", email_error: "", password_error: "Please enter at least 5 characters", server_error: ""});
+ this.setState({nameError: '', emailError: '', passwordError: 'Please enter at least 5 characters', serverError: ''});
return;
}
- this.setState({name_error: "", email_error: "", password_error: "", server_error: ""});
+ this.setState({nameError: '', emailError: '', passwordError: '', serverError: ''});
this.state.user.allow_marketing = true;
@@ -50,108 +54,154 @@ module.exports = React.createClass({
function(data) {
UserStore.setLastEmail(this.state.user.email);
UserStore.setCurrentUser(data);
- if (this.props.hash > 0)
- {
- BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: "finished"}));
+ if (this.props.hash > 0) {
+ BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'}));
}
window.location.href = '/';
}.bind(this),
function(err) {
- if (err.message == "Login failed because email address has not been verified") {
- window.location.href = "/verify_email?email="+ encodeURIComponent(this.state.user.email) + "&teamname=" + encodeURIComponent(this.props.teamName);
+ if (err.message === 'Login failed because email address has not been verified') {
+ window.location.href = '/verify_email?email=' + encodeURIComponent(this.state.user.email) + '&teamname=' + encodeURIComponent(this.props.teamName);
} else {
- this.state.server_error = err.message;
- this.setState(this.state);
+ this.setState({serverError: err.message});
}
}.bind(this)
);
}.bind(this),
function(err) {
- this.state.server_error = err.message;
- this.setState(this.state);
+ this.setState({serverError: err.message});
}.bind(this)
);
},
getInitialState: function() {
- var props = BrowserStore.getGlobalItem(this.props.hash);
-
- if (!props) {
- props = {};
- props.wizard = "welcome";
- props.user = {};
- props.user.team_id = this.props.teamId;
- props.user.email = this.props.email;
- props.hash = this.props.hash;
- props.data = this.props.data;
- props.original_email = this.props.email;
+ var state = BrowserStore.getGlobalItem(this.props.hash);
+
+ if (!state) {
+ state = {};
+ state.wizard = 'welcome';
+ state.user = {};
+ state.user.team_id = this.props.teamId;
+ state.user.email = this.props.email;
+ state.hash = this.props.hash;
+ state.data = this.props.data;
+ state.original_email = this.props.email;
}
- return props;
+ return state;
},
render: function() {
-
client.track('signup', 'signup_user_01_welcome');
- if (this.state.wizard == "finished") {
- return (<div>You've already completed the signup process for this invitation or this invitation has expired.</div>);
+ if (this.state.wizard === 'finished') {
+ return <div>You've already completed the signup process for this invitation or this invitation has expired.</div>;
+ }
+
+ // set up error labels
+ var emailError = null;
+ var emailDivStyle = 'form-group';
+ if (this.state.emailError) {
+ emailError = <label className='control-label'>{this.state.emailError}</label>;
+ emailDivStyle += ' has-error';
+ }
+
+ var nameError = null;
+ var nameDivStyle = 'form-group';
+ if (this.state.nameError) {
+ nameError = <label className='control-label'>{this.state.nameError}</label>;
+ nameDivStyle += ' has-error';
+ }
+
+ var passwordError = null;
+ var passwordDivStyle = 'form-group';
+ if (this.state.passwordError) {
+ passwordError = <label className='control-label'>{this.state.passwordError}</label>;
+ passwordDivStyle += ' has-error';
+ }
+
+ var serverError = null;
+ if (this.state.serverError) {
+ serverError = (
+ <div className={'form-group has-error'}>
+ <label className='control-label'>{this.state.serverError}</label>
+ </div>
+ );
}
- var email_error = this.state.email_error ? <label className='control-label'>{ this.state.email_error }</label> : null;
- var name_error = this.state.name_error ? <label className='control-label'>{ this.state.name_error }</label> : null;
- var password_error = this.state.password_error ? <label className='control-label'>{ this.state.password_error }</label> : null;
- var server_error = this.state.server_error ? <div className={ "form-group has-error" }><label className='control-label'>{ this.state.server_error }</label></div> : null;
+ // set up the email entry and hide it if an email was provided
+ var yourEmailIs = '';
+ if (this.state.user.email) {
+ yourEmailIs = <span>Your email address is {this.state.user.email}. You'll use this address to sign in to {config.SiteName}.</span>;
+ }
- var yourEmailIs = this.state.user.email == "" ? "" : <span>Your email address is { this.state.user.email }. </span>
+ var emailContainerStyle = "margin--extra";
+ if (this.state.original_email) {
+ emailContainerStyle = "hidden";
+ }
var email = (
- <div className={ this.state.original_email == "" ? "margin--extra" : "hidden"} >
+ <div className={emailContainerStyle}>
<h5><strong>What's your email address?</strong></h5>
- <div className={ email_error ? "form-group has-error" : "form-group" }>
- <input type="email" ref="email" className="form-control" defaultValue={ this.state.user.email } placeholder="" maxLength="128" autoFocus={true} />
- { email_error }
- </div>
+ <div className={emailDivStyle}>
+ <input type='email' ref='email' className='form-control' defaultValue={this.state.user.email} placeholder='' maxLength='128' autoFocus={true} />
+ {emailError}
</div>
+ </div>
);
- var auth_services = JSON.parse(this.props.authServices);
+ // add options to log in using another service
+ var authServices = JSON.parse(this.props.authServices);
+
+ var signupMessage = null;
+ if (authServices.indexOf('gitlab') >= 0) {
+ signupMessage = (
+ <div>
+ <a className='btn btn-custom-login gitlab' href={'/' + this.props.teamName + '/signup/gitlab' + window.location.search}>
+ <span className='icon' />
+ <span>with GitLab</span>
+ </a>
+ <div className='or__container'>
+ <span>or</span>
+ </div>
+ </div>
+ );
+ }
- var signup_message;
- if (auth_services.indexOf("gitlab") >= 0) {
- signup_message = <div><a className="btn btn-custom-login gitlab" href={"/"+this.props.teamName+"/signup/gitlab"+window.location.search}><span className="icon" />{"with GitLab"}</a>
- <div className="or__container"><span>or</span></div></div>;
+ var termsDisclaimer = null;
+ if (config.ShowTermsDuringSignup) {
+ termsDisclaimer = <p>By creating an account and using Mattermost you are agreeing to our <a href={config.TermsLink}>Terms of Service</a>. If you do not agree, you cannot use this service.</p>;
}
return (
<div>
<form>
- <img className="signup-team-logo" src="/static/images/logo.png" />
- <h5 className="margin--less">Welcome to:</h5>
- <h2 className="signup-team__name">{ this.props.teamDisplayName }</h2>
- <h2 className="signup-team__subdomain">on { config.SiteName }</h2>
- <h4 className="color--light">Let's create your account</h4>
- { signup_message }
- <div className="inner__content">
- { email }
- <p className={ this.state.original_email == "" ? "hidden" : ""}>{ yourEmailIs } You’ll use this address to sign in to {config.SiteName}.</p>
- <div className="margin--extra">
- <h5><strong>Choose your username</strong></h5>
- <div className={ name_error ? "form-group has-error" : "form-group" }>
- <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" />
- { name_error }
- <p className="form__hint">Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'"</p>
+ <img className='signup-team-logo' src='/static/images/logo.png' />
+ <h5 className='margin--less'>Welcome to:</h5>
+ <h2 className='signup-team__name'>{this.props.teamDisplayName}</h2>
+ <h2 className='signup-team__subdomain'>on {config.SiteName}</h2>
+ <h4 className='color--light'>Let's create your account</h4>
+ {signupMessage}
+ <div className='inner__content'>
+ {email}
+ {yourEmailIs}
+ <div className='margin--extra'>
+ <h5><strong>Choose your username</strong></h5>
+ <div className={nameDivStyle}>
+ <input type='text' ref='name' className='form-control' placeholder='' maxLength='128' />
+ {nameError}
+ <p className='form__hint'>Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'</p>
+ </div>
+ </div>
+ <div className='margin--extra'>
+ <h5><strong>Choose your password</strong></h5>
+ <div className={passwordDivStyle}>
+ <input type='password' ref='password' className='form-control' placeholder='' maxLength='128' />
+ {passwordError}
</div>
- </div>
- <div className="margin--extra">
- <h5><strong>Choose your password</strong></h5>
- <div className={ password_error ? "form-group has-error" : "form-group" }>
- <input type="password" ref="password" className="form-control" placeholder="" maxLength="128" />
- { password_error }
</div>
</div>
- </div>
- <p className="margin--extra"><button type='submit' onClick={this.handleSubmit} className="btn-primary btn">Create Account</button></p>
- { server_error }
- <p>By creating an account and using Mattermost you are agreeing to our <a href={ config.TermsLink }>Terms of Service</a>. If you do not agree, you cannot use this service.</p>
+ <p className='margin--extra'><button type='submit' onClick={this.handleSubmit} className='btn-primary btn'>Create Account</button></p>
+ {serverError}
+ {termsDisclaimer}
</form>
</div>
);
diff --git a/web/react/components/team_settings_modal.jsx b/web/react/components/team_settings_modal.jsx
index e50378b7f..b1c38fd16 100644
--- a/web/react/components/team_settings_modal.jsx
+++ b/web/react/components/team_settings_modal.jsx
@@ -29,7 +29,7 @@ module.exports = React.createClass({
tabs.push({name: "feature", ui_name: "Features", icon: "glyphicon glyphicon-wrench"});
return (
- <div className="modal fade" ref="modal" id="team_settings" role="dialog" aria-hidden="true">
+ <div className="modal fade" ref="modal" id="team_settings" role="dialog" tabIndex="-1" aria-hidden="true">
<div className="modal-dialog settings-modal">
<div className="modal-content">
<div className="modal-header">
diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx
index bbd1f84b6..b5c5cc564 100644
--- a/web/react/components/textbox.jsx
+++ b/web/react/components/textbox.jsx
@@ -2,11 +2,7 @@
// See License.txt for license information.
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
-var UserStore = require('../stores/user_store.jsx');
var PostStore = require('../stores/post_store.jsx');
-var SocketStore = require('../stores/socket_store.jsx');
-var MsgTyping = require('./msg_typing.jsx');
-var MentionList = require('./mention_list.jsx');
var CommandList = require('./command_list.jsx');
var ErrorStore = require('../stores/error_store.jsx');
var AsyncClient = require('../utils/async_client.jsx');
@@ -19,53 +15,53 @@ function getStateFromStores() {
var error = ErrorStore.getLastError();
if (error) {
- return { message: error.message };
- } else {
- return { message: null };
+ return {message: error.message};
}
+ return {message: null};
}
module.exports = React.createClass({
+ displayName: 'Textbox',
caret: -1,
addedMention: false,
doProcessMentions: false,
mentions: [],
componentDidMount: function() {
- PostStore.addAddMentionListener(this._onChange);
- ErrorStore.addChangeListener(this._onError);
+ PostStore.addAddMentionListener(this.onListenerChange);
+ ErrorStore.addChangeListener(this.onRecievedError);
this.resize();
- this.processMentions();
+ this.updateMentionTab(null);
},
componentWillUnmount: function() {
- PostStore.removeAddMentionListener(this._onChange);
- ErrorStore.removeChangeListener(this._onError);
+ PostStore.removeAddMentionListener(this.onListenerChange);
+ ErrorStore.removeChangeListener(this.onRecievedError);
},
- _onChange: function(id, username) {
- if (id !== this.props.id) return;
- this.addMention(username);
+ onListenerChange: function(id, username) {
+ if (id === this.props.id) {
+ this.addMention(username);
+ }
},
- _onError: function() {
+ onRecievedError: function() {
var errorState = getStateFromStores();
if (this.state.timerInterrupt != null) {
window.clearInterval(this.state.timerInterrupt);
- this.setState({ timerInterrupt: null });
+ this.setState({timerInterrupt: null});
}
- if (errorState.message === "There appears to be a problem with your internet connection") {
- this.setState({ connection: "bad-connection" });
- var timerInterrupt = window.setInterval(this._onTimerInterrupt, 5000);
- this.setState({ timerInterrupt: timerInterrupt });
- }
- else {
- this.setState({ connection: "" });
+ if (errorState.message === 'There appears to be a problem with your internet connection') {
+ this.setState({connection: 'bad-connection'});
+ var timerInterrupt = window.setInterval(this.onTimerInterrupt, 5000);
+ this.setState({timerInterrupt: timerInterrupt});
+ } else {
+ this.setState({connection: ''});
}
},
- _onTimerInterrupt: function() {
+ onTimerInterrupt: function() {
//Since these should only happen when you have no connection and slightly briefly after any
//performance hit should not matter
- if (this.state.connection === "bad-connection") {
+ if (this.state.connection === 'bad-connection') {
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_ERROR,
err: null
@@ -75,15 +71,15 @@ module.exports = React.createClass({
}
window.clearInterval(this.state.timerInterrupt);
- this.setState({ timerInterrupt: null });
+ this.setState({timerInterrupt: null});
},
componentDidUpdate: function() {
if (this.caret >= 0) {
- utils.setCaretPosition(this.refs.message.getDOMNode(), this.caret)
+ utils.setCaretPosition(this.refs.message.getDOMNode(), this.caret);
this.caret = -1;
}
if (this.doProcessMentions) {
- this.processMentions();
+ this.updateMentionTab(null);
this.doProcessMentions = false;
}
this.resize();
@@ -93,7 +89,7 @@ module.exports = React.createClass({
this.checkForNewMention(nextProps.messageText);
}
var text = this.refs.message.getDOMNode().value;
- if (nextProps.channelId != this.props.channelId || nextProps.messageText !== text) {
+ if (nextProps.channelId !== this.props.channelId || nextProps.messageText !== text) {
this.doProcessMentions = true;
}
this.addedMention = false;
@@ -101,17 +97,17 @@ module.exports = React.createClass({
this.resize();
},
getInitialState: function() {
- return { mentionText: '-1', mentions: [], connection: "", timerInterrupt: null };
+ return {mentionText: '-1', mentions: [], connection: '', timerInterrupt: null};
},
- updateMentionTab: function(mentionText, excludeList) {
+ updateMentionTab: function(mentionText) {
var self = this;
+
// using setTimeout so dispatch isn't called during an in progress dispatch
setTimeout(function() {
AppDispatcher.handleViewAction({
type: ActionTypes.RECIEVED_MENTION_DATA,
id: self.props.id,
- mention_text: mentionText,
- exclude_list: excludeList
+ mention_text: mentionText
});
}, 1);
},
@@ -122,13 +118,13 @@ module.exports = React.createClass({
handleKeyPress: function(e) {
var text = this.refs.message.getDOMNode().value;
- if (!this.refs.commands.isEmpty() && text.indexOf("/") == 0 && e.which==13) {
+ if (!this.refs.commands.isEmpty() && text.indexOf('/') === 0 && e.which === 13) {
this.refs.commands.addFirstCommand();
e.preventDefault();
return;
}
- if ( !this.doProcessMentions) {
+ if (!this.doProcessMentions) {
var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
var preText = text.substring(0, caret);
var lastSpace = preText.lastIndexOf(' ');
@@ -150,13 +146,15 @@ module.exports = React.createClass({
this.handleBackspace(e);
}
},
- handleBackspace: function(e) {
+ handleBackspace: function() {
var text = this.refs.message.getDOMNode().value;
- if (text.indexOf("/") == 0) {
- this.refs.commands.getSuggestedCommands(text.substring(0, text.length-1));
+ if (text.indexOf('/') === 0) {
+ this.refs.commands.getSuggestedCommands(text.substring(0, text.length - 1));
}
- if (this.doProcessMentions) return;
+ if (this.doProcessMentions) {
+ return;
+ }
var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
var preText = text.substring(0, caret);
@@ -167,57 +165,6 @@ module.exports = React.createClass({
this.doProcessMentions = true;
}
},
- processMentions: function() {
- /* First, find all the possible mentions and add
- them all to a list of mentions */
- var text = utils.insertHtmlEntities(this.refs.message.getDOMNode().value);
-
- var profileMap = UserStore.getProfilesUsernameMap();
-
- var re1 = /@([a-z0-9_]+)( |$|\n)/gi;
-
- var matches = text.match(re1);
-
- if (!matches) {
- this.updateMentionTab(null, []);
- return;
- }
-
- var mentions = [];
- for (var i = 0; i < matches.length; i++) {
- var m = matches[i].substring(1,matches[i].length).trim();
- if ((m in profileMap && mentions.indexOf(m) === -1) || Constants.SPECIAL_MENTIONS.indexOf(m) !== -1) {
- mentions.push(m);
- }
- }
-
- /* Figure out what the user is currently typing. If it's a mention then we don't
- want to add it to the mention list yet, so we remove it if
- there is only one occurence of that mention so far. */
- var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
-
- var text = this.props.messageText;
-
- var preText = text.substring(0, caret);
-
- var atIndex = preText.lastIndexOf('@');
- var spaceIndex = preText.lastIndexOf(' ');
- var newLineIndex = preText.lastIndexOf('\n');
-
- var typingMention = "";
- if (atIndex > spaceIndex && atIndex > newLineIndex) {
-
- typingMention = text.substring(atIndex+1, caret);
- }
-
- var re2 = new RegExp('@' + typingMention + '( |$|\n)', 'g');
-
- if ((text.match(re2) || []).length === 1 && mentions.indexOf(typingMention) !== -1) {
- mentions.splice(mentions.indexOf(typingMention), 1);
- }
-
- this.updateMentionTab(null, mentions);
- },
checkForNewMention: function(text) {
var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
@@ -227,7 +174,7 @@ module.exports = React.createClass({
// The @ character not typed, so nothing to do.
if (atIndex === -1) {
- this.updateMentionTab('-1', null);
+ this.updateMentionTab('-1');
return;
}
@@ -236,13 +183,13 @@ module.exports = React.createClass({
// If there is a space after the last @, nothing to do.
if (lastSpace > atIndex || lastCharSpace > atIndex) {
- this.updateMentionTab('-1', null);
+ this.updateMentionTab('-1');
return;
}
// Get the name typed so far.
- var name = preText.substring(atIndex+1, preText.length).toLowerCase();
- this.updateMentionTab(name, null);
+ var name = preText.substring(atIndex + 1, preText.length).toLowerCase();
+ this.updateMentionTab(name);
},
addMention: function(name) {
var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
@@ -264,7 +211,7 @@ module.exports = React.createClass({
this.addedMention = true;
this.doProcessMentions = true;
- this.props.onUserInput(prefix + "@" + name + " " + suffix);
+ this.props.onUserInput(prefix + '@' + name + ' ' + suffix);
},
addCommand: function(cmd) {
var elm = this.refs.message.getDOMNode();
@@ -275,22 +222,26 @@ module.exports = React.createClass({
var e = this.refs.message.getDOMNode();
var w = this.refs.wrapper.getDOMNode();
- var lht = parseInt($(e).css('lineHeight'),10);
+ var lht = parseInt($(e).css('lineHeight'), 10);
var lines = e.scrollHeight / lht;
- var mod = lines < 2.5 || this.props.messageText === "" ? 30 : 15;
+ var mod = 15;
+
+ if (lines < 2.5 || this.props.messageText === '') {
+ mod = 30;
+ }
if (e.scrollHeight - mod < 167) {
- $(e).css({'height':'auto','overflow-y':'hidden'}).height(e.scrollHeight - mod);
- $(w).css({'height':'auto'}).height(e.scrollHeight+2);
+ $(e).css({height: 'auto', 'overflow-y': 'hidden'}).height(e.scrollHeight - mod);
+ $(w).css({height: 'auto'}).height(e.scrollHeight + 2);
} else {
- $(e).css({'height':'auto','overflow-y':'scroll'}).height(167);
- $(w).css({'height':'auto'}).height(167);
+ $(e).css({height: 'auto', 'overflow-y': 'scroll'}).height(167);
+ $(w).css({height: 'auto'}).height(167);
}
},
handleFocus: function() {
var elm = this.refs.message.getDOMNode();
if (elm.title === elm.value) {
- elm.value = "";
+ elm.value = '';
}
},
handleBlur: function() {
@@ -304,9 +255,9 @@ module.exports = React.createClass({
},
render: function() {
return (
- <div ref="wrapper" className="textarea-wrapper">
+ <div ref='wrapper' className='textarea-wrapper'>
<CommandList ref='commands' addCommand={this.addCommand} channelId={this.props.channelId} />
- <textarea id={this.props.id} ref="message" className={"form-control custom-textarea " + this.state.connection} spellCheck="true" autoComplete="off" autoCorrect="off" rows="1" placeholder={this.props.createMessage} value={this.props.messageText} onInput={this.handleChange} onChange={this.handleChange} onKeyPress={this.handleKeyPress} onKeyDown={this.handleKeyDown} onFocus={this.handleFocus} onBlur={this.handleBlur} onPaste={this.handlePaste} />
+ <textarea id={this.props.id} ref='message' className={'form-control custom-textarea ' + this.state.connection} spellCheck='true' autoComplete='off' autoCorrect='off' rows='1' placeholder={this.props.createMessage} value={this.props.messageText} onInput={this.handleChange} onChange={this.handleChange} onKeyPress={this.handleKeyPress} onKeyDown={this.handleKeyDown} onFocus={this.handleFocus} onBlur={this.handleBlur} onPaste={this.handlePaste} />
</div>
);
}
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
index 65f025919..5c4d26a23 100644
--- a/web/react/components/user_profile.jsx
+++ b/web/react/components/user_profile.jsx
@@ -28,6 +28,7 @@ module.exports = React.createClass({
componentDidMount: function() {
UserStore.addChangeListener(this._onChange);
$("#profile_" + this.uniqueId).popover({placement : 'right', container: 'body', trigger: 'hover', html: true, delay: { "show": 200, "hide": 100 }});
+ $('body').tooltip( {selector: '[data-toggle=tooltip]', trigger: 'hover click'} );
},
componentWillUnmount: function() {
UserStore.removeChangeListener(this._onChange);
@@ -57,7 +58,7 @@ module.exports = React.createClass({
if (!config.ShowEmail) {
data_content += "<div class='text-nowrap'>Email not shared</div>";
} else {
- data_content += "<div><a href='mailto:" + this.state.profile.email + "' class='text-nowrap text-lowercase'>" + this.state.profile.email + "</a></div>";
+ data_content += "<div data-toggle='tooltip' title= '" + this.state.profile.email + "'><a href='mailto:" + this.state.profile.email + "' class='text-nowrap text-lowercase user-popover__email'>" + this.state.profile.email + "</a></div>";
}
return (
diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx
index 95d1178d1..1a0c313d3 100644
--- a/web/react/components/user_settings.jsx
+++ b/web/react/components/user_settings.jsx
@@ -5,8 +5,6 @@ var UserStore = require('../stores/user_store.jsx');
var SettingItemMin = require('./setting_item_min.jsx');
var SettingItemMax = require('./setting_item_max.jsx');
var SettingPicture = require('./setting_picture.jsx');
-var AccessHistoryModal = require('./access_history_modal.jsx');
-var ActivityLogModal = require('./activity_log_modal.jsx');
var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var utils = require('../utils/utils.jsx');
@@ -102,6 +100,8 @@ var NotificationsTab = React.createClass({
});
this.setState(assign({},getNotificationsStateFromStores(),{server_error: null}));
+
+ this.props.updateTab('general');
},
componentDidMount: function() {
UserStore.addChangeListener(this._onChange);
@@ -110,6 +110,7 @@ var NotificationsTab = React.createClass({
componentWillUnmount: function() {
UserStore.removeChangeListener(this._onChange);
$('#user_settings1').off('hidden.bs.modal', this.handleClose);
+ this.props.updateSection('');
},
_onChange: function() {
var newState = getNotificationsStateFromStores();
@@ -460,22 +461,22 @@ var SecurityTab = React.createClass({
e.preventDefault();
var user = this.props.user;
- var currentPassword = this.state.current_password;
- var newPassword = this.state.new_password;
- var confirmPassword = this.state.confirm_password;
+ var currentPassword = this.state.currentPassword;
+ var newPassword = this.state.newPassword;
+ var confirmPassword = this.state.confirmPassword;
if (currentPassword === '') {
- this.setState({password_error: 'Please enter your current password', server_error: ''});
+ this.setState({passwordError: 'Please enter your current password', serverError: ''});
return;
}
if (newPassword.length < 5) {
- this.setState({password_error: 'New passwords must be at least 5 characters', server_error: ''});
+ this.setState({passwordError: 'New passwords must be at least 5 characters', serverError: ''});
return;
}
if (newPassword !== confirmPassword) {
- this.setState({password_error: 'The new passwords you entered do not match', server_error: ''});
+ this.setState({passwordError: 'The new passwords you entered do not match', serverError: ''});
return;
}
@@ -485,85 +486,88 @@ var SecurityTab = React.createClass({
data.new_password = newPassword;
client.updatePassword(data,
- function(data) {
- this.props.updateSection("");
+ function() {
+ this.props.updateSection('');
AsyncClient.getMe();
- this.setState({current_password: '', new_password: '', confirm_password: ''});
+ this.setState({currentPassword: '', newPassword: '', confirmPassword: ''});
}.bind(this),
function(err) {
var state = this.getInitialState();
if (err.message) {
- state.server_error = err.message;
+ state.serverError = err.message;
} else {
- state.server_error = err;
+ state.serverError = err;
}
- state.password_error = '';
+ state.passwordError = '';
this.setState(state);
}.bind(this)
);
},
updateCurrentPassword: function(e) {
- this.setState({ current_password: e.target.value });
+ this.setState({currentPassword: e.target.value});
},
updateNewPassword: function(e) {
- this.setState({ new_password: e.target.value });
+ this.setState({newPassword: e.target.value});
},
updateConfirmPassword: function(e) {
- this.setState({ confirm_password: e.target.value });
+ this.setState({confirmPassword: e.target.value});
},
handleHistoryOpen: function() {
- $("#user_settings1").modal('hide');
+ $('#user_settings1').modal('hide');
},
handleDevicesOpen: function() {
- $("#user_settings1").modal('hide');
+ $('#user_settings1').modal('hide');
},
handleClose: function() {
- $(this.getDOMNode()).find(".form-control").each(function() {
- this.value = "";
+ $(this.getDOMNode()).find('.form-control').each(function() {
+ this.value = '';
});
- this.setState({current_password: '', new_password: '', confirm_password: '', server_error: null, password_error: null});
+ this.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null});
+ this.props.updateTab('general');
},
componentDidMount: function() {
$('#user_settings1').on('hidden.bs.modal', this.handleClose);
},
componentWillUnmount: function() {
$('#user_settings1').off('hidden.bs.modal', this.handleClose);
+ this.props.updateSection('');
},
getInitialState: function() {
- return { current_password: '', new_password: '', confirm_password: '' };
+ return {currentPassword: '', newPassword: '', confirmPassword: ''};
},
render: function() {
- var server_error = this.state.server_error ? this.state.server_error : null;
- var password_error = this.state.password_error ? this.state.password_error : null;
+ var serverError = this.state.serverError ? this.state.serverError : null;
+ var passwordError = this.state.passwordError ? this.state.passwordError : null;
+ var updateSectionStatus;
var passwordSection;
var self = this;
if (this.props.activeSection === 'password') {
var inputs = [];
var submit = null;
- if (this.props.user.auth_service === "") {
+ if (this.props.user.auth_service === '') {
inputs.push(
- <div className="form-group">
- <label className="col-sm-5 control-label">Current Password</label>
- <div className="col-sm-7">
- <input className="form-control" type="password" onChange={this.updateCurrentPassword} value={this.state.current_password}/>
+ <div className='form-group'>
+ <label className='col-sm-5 control-label'>Current Password</label>
+ <div className='col-sm-7'>
+ <input className='form-control' type='password' onChange={this.updateCurrentPassword} value={this.state.currentPassword}/>
</div>
</div>
);
inputs.push(
- <div className="form-group">
- <label className="col-sm-5 control-label">New Password</label>
- <div className="col-sm-7">
- <input className="form-control" type="password" onChange={this.updateNewPassword} value={this.state.new_password}/>
+ <div className='form-group'>
+ <label className='col-sm-5 control-label'>New Password</label>
+ <div className='col-sm-7'>
+ <input className='form-control' type='password' onChange={this.updateNewPassword} value={this.state.newPassword}/>
</div>
</div>
);
inputs.push(
- <div className="form-group">
- <label className="col-sm-5 control-label">Retype New Password</label>
- <div className="col-sm-7">
- <input className="form-control" type="password" onChange={this.updateConfirmPassword} value={this.state.confirm_password}/>
+ <div className='form-group'>
+ <label className='col-sm-5 control-label'>Retype New Password</label>
+ <div className='col-sm-7'>
+ <input className='form-control' type='password' onChange={this.updateConfirmPassword} value={this.state.confirmPassword}/>
</div>
</div>
);
@@ -571,58 +575,68 @@ var SecurityTab = React.createClass({
submit = this.submitPassword;
} else {
inputs.push(
- <div className="form-group">
- <label className="col-sm-12">Log in occurs through GitLab. Please see your GitLab account settings page to update your password.</label>
+ <div className='form-group'>
+ <label className='col-sm-12'>Log in occurs through GitLab. Please see your GitLab account settings page to update your password.</label>
</div>
);
}
+ updateSectionStatus = function(e) {
+ self.props.updateSection('');
+ self.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null});
+ e.preventDefault();
+ };
+
passwordSection = (
<SettingItemMax
- title="Password"
+ title='Password'
inputs={inputs}
submit={submit}
- server_error={server_error}
- client_error={password_error}
- updateSection={function(e){self.props.updateSection("");e.preventDefault();}}
+ server_error={serverError}
+ client_error={passwordError}
+ updateSection={updateSectionStatus}
/>
);
} else {
var describe;
- if (this.props.user.auth_service === "") {
+ if (this.props.user.auth_service === '') {
var d = new Date(this.props.user.last_password_update);
- var hour = d.getHours() % 12 ? String(d.getHours() % 12) : "12";
- var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes());
- var timeOfDay = d.getHours() >= 12 ? " pm" : " am";
- describe = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min + timeOfDay;
+ var hour = d.getHours() % 12 ? String(d.getHours() % 12) : '12';
+ var min = d.getMinutes() < 10 ? '0' + d.getMinutes() : String(d.getMinutes());
+ var timeOfDay = d.getHours() >= 12 ? ' pm' : ' am';
+ describe = 'Last updated ' + Constants.MONTHS[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear() + ' at ' + hour + ':' + min + timeOfDay;
} else {
- describe = "Log in done through GitLab"
+ describe = 'Log in done through GitLab';
}
+ updateSectionStatus = function() {
+ self.props.updateSection('password');
+ };
+
passwordSection = (
<SettingItemMin
- title="Password"
+ title='Password'
describe={describe}
- updateSection={function(){self.props.updateSection("password");}}
+ updateSection={updateSectionStatus}
/>
);
}
return (
<div>
- <div className="modal-header">
- <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- <h4 className="modal-title" ref="title"><i className="modal-back"></i>Security Settings</h4>
+ <div className='modal-header'>
+ <button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button>
+ <h4 className='modal-title' ref='title'><i className='modal-back'></i>Security Settings</h4>
</div>
- <div className="user-settings">
- <h3 className="tab-header">Security Settings</h3>
- <div className="divider-dark first"/>
- { passwordSection }
- <div className="divider-dark"/>
+ <div className='user-settings'>
+ <h3 className='tab-header'>Security Settings</h3>
+ <div className='divider-dark first'/>
+ {passwordSection}
+ <div className='divider-dark'/>
<br></br>
- <a data-toggle="modal" className="security-links theme" data-target="#access-history" href="#" onClick={this.handleHistoryOpen}><i className="fa fa-clock-o"></i>View Access History</a>
- <b> </b>
- <a data-toggle="modal" className="security-links theme" data-target="#activity-log" href="#" onClick={this.handleDevicesOpen}><i className="fa fa-globe"></i>View and Logout of Active Sessions</a>
+ <a data-toggle='modal' className='security-links theme' data-target='#access-history' href='#' onClick={this.handleHistoryOpen}><i className='fa fa-clock-o'></i>View Access History</a>
+ <b> </b>
+ <a data-toggle='modal' className='security-links theme' data-target='#activity-log' href='#' onClick={this.handleDevicesOpen}><i className='fa fa-globe'></i>View and Logout of Active Sessions</a>
</div>
</div>
);
@@ -637,17 +651,17 @@ var GeneralTab = React.createClass({
var user = this.props.user;
var username = this.state.username.trim();
- var username_error = utils.isValidUsername(username);
- if (username_error === 'Cannot use a reserved word as a username.') {
- this.setState({client_error: 'This username is reserved, please choose a new one.' });
+ var usernameError = utils.isValidUsername(username);
+ if (usernameError === 'Cannot use a reserved word as a username.') {
+ this.setState({clientError: 'This username is reserved, please choose a new one.'});
return;
- } else if (username_error) {
- this.setState({client_error: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'." });
+ } else if (usernameError) {
+ this.setState({clientError: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."});
return;
}
if (user.username === username) {
- this.setState({client_error: 'You must submit a new username'});
+ this.setState({clientError: 'You must submit a new username'});
return;
}
@@ -662,7 +676,7 @@ var GeneralTab = React.createClass({
var nickname = this.state.nickname.trim();
if (user.nickname === nickname) {
- this.setState({client_error: 'You must submit a new nickname'})
+ this.setState({clientError: 'You must submit a new nickname'});
return;
}
@@ -674,11 +688,11 @@ var GeneralTab = React.createClass({
e.preventDefault();
var user = UserStore.getCurrentUser();
- var firstName = this.state.first_name.trim();
- var lastName = this.state.last_name.trim();
+ var firstName = this.state.firstName.trim();
+ var lastName = this.state.lastName.trim();
if (user.first_name === firstName && user.last_name === lastName) {
- this.setState({client_error: 'You must submit a new first or last name'})
+ this.setState({clientError: 'You must submit a new first or last name'});
return;
}
@@ -698,7 +712,7 @@ var GeneralTab = React.createClass({
}
if (email === '' || !utils.isEmail(email)) {
- this.setState({ email_error: 'Please enter a valid email address' });
+ this.setState({emailError: 'Please enter a valid email address'});
return;
}
@@ -713,11 +727,11 @@ var GeneralTab = React.createClass({
AsyncClient.getMe();
}.bind(this),
function(err) {
- state = this.getInitialState();
+ var state = this.getInitialState();
if (err.message) {
- state.server_error = err.message;
+ state.serverError = err.message;
} else {
- state.server_error = err;
+ state.serverError = err;
}
this.setState(state);
}.bind(this)
@@ -737,12 +751,13 @@ var GeneralTab = React.createClass({
var picture = this.state.picture;
if (picture.type !== 'image/jpeg' && picture.type !== 'image/png') {
- this.setState({client_error: 'Only JPG or PNG images may be used for profile pictures'});
+ this.setState({clientError: 'Only JPG or PNG images may be used for profile pictures'});
return;
}
var formData = new FormData();
formData.append('image', picture, picture.name);
+ this.setState({loadingPicture: true});
client.uploadProfileImage(formData,
function() {
@@ -751,8 +766,8 @@ var GeneralTab = React.createClass({
window.location.reload();
}.bind(this),
function(err) {
- state = this.getInitialState();
- state.server_error = err;
+ var state = this.getInitialState();
+ state.serverError = err;
this.setState(state);
}.bind(this)
);
@@ -761,10 +776,10 @@ var GeneralTab = React.createClass({
this.setState({username: e.target.value});
},
updateFirstName: function(e) {
- this.setState({first_name: e.target.value});
+ this.setState({firstName: e.target.value});
},
updateLastName: function(e) {
- this.setState({last_name: e.target.value});
+ this.setState({lastName: e.target.value});
},
updateNickname: function(e) {
this.setState({nickname: e.target.value});
@@ -774,17 +789,16 @@ var GeneralTab = React.createClass({
},
updatePicture: function(e) {
if (e.target.files && e.target.files[0]) {
- this.setState({ picture: e.target.files[0] });
+ this.setState({picture: e.target.files[0]});
this.submitActive = true;
- this.setState({client_error: null});
-
+ this.setState({clientError: null});
} else {
this.setState({picture: null});
}
},
updateSection: function(section) {
- this.setState({client_error:''});
+ this.setState({clientError: ''});
this.submitActive = false;
this.props.updateSection(section);
},
@@ -793,7 +807,8 @@ var GeneralTab = React.createClass({
this.value = '';
});
- this.setState(assign({}, this.getInitialState(), {client_error: null, server_error: null, email_error: null}));
+ this.setState(assign({}, this.getInitialState(), {clientError: null, serverError: null, emailError: null}));
+ this.props.updateSection('');
},
componentDidMount: function() {
$('#user_settings1').on('hidden.bs.modal', this.handleClose);
@@ -804,15 +819,24 @@ var GeneralTab = React.createClass({
getInitialState: function() {
var user = this.props.user;
- return { username: user.username, first_name: user.first_name, last_name: user.last_name, nickname: user.nickname,
- email: user.email, picture: null };
+ return {username: user.username, firstName: user.first_name, lastName: user.last_name, nickname: user.nickname,
+ email: user.email, picture: null, loadingPicture: false};
},
render: function() {
var user = this.props.user;
- var client_error = this.state.client_error ? this.state.client_error : null;
- var server_error = this.state.server_error ? this.state.server_error : null;
- var email_error = this.state.email_error ? this.state.email_error : null;
+ var clientError = null;
+ if (this.state.clientError) {
+ clientError = this.state.clientError;
+ }
+ var serverError = null;
+ if (this.state.serverError) {
+ serverError = this.state.serverError;
+ }
+ var emailError = null;
+ if (this.state.emailError) {
+ emailError = this.state.emailError;
+ }
var nameSection;
var self = this;
@@ -823,7 +847,7 @@ var GeneralTab = React.createClass({
<div className='form-group'>
<label className='col-sm-5 control-label'>First Name</label>
<div className='col-sm-7'>
- <input className='form-control' type='text' onChange={this.updateFirstName} value={this.state.first_name}/>
+ <input className='form-control' type='text' onChange={this.updateFirstName} value={this.state.firstName}/>
</div>
</div>
);
@@ -832,7 +856,7 @@ var GeneralTab = React.createClass({
<div className='form-group'>
<label className='col-sm-5 control-label'>Last Name</label>
<div className='col-sm-7'>
- <input className='form-control' type='text' onChange={this.updateLastName} value={this.state.last_name}/>
+ <input className='form-control' type='text' onChange={this.updateLastName} value={this.state.lastName}/>
</div>
</div>
);
@@ -842,8 +866,8 @@ var GeneralTab = React.createClass({
title='Full Name'
inputs={inputs}
submit={this.submitName}
- server_error={server_error}
- client_error={client_error}
+ server_error={serverError}
+ client_error={clientError}
updateSection={function(e) {
self.updateSection('');
e.preventDefault();
@@ -851,20 +875,20 @@ var GeneralTab = React.createClass({
/>
);
} else {
- var full_name = '';
+ var fullName = '';
if (user.first_name && user.last_name) {
- full_name = user.first_name + ' ' + user.last_name;
+ fullName = user.first_name + ' ' + user.last_name;
} else if (user.first_name) {
- full_name = user.first_name;
+ fullName = user.first_name;
} else if (user.last_name) {
- full_name = user.last_name;
+ fullName = user.last_name;
}
nameSection = (
<SettingItemMin
title='Full Name'
- describe={full_name}
+ describe={fullName}
updateSection={function() {
self.updateSection('name');
}}
@@ -874,7 +898,6 @@ var GeneralTab = React.createClass({
var nicknameSection;
if (this.props.activeSection === 'nickname') {
-
inputs.push(
<div className='form-group'>
<label className='col-sm-5 control-label'>{utils.isMobile() ? '' : 'Nickname'}</label>
@@ -889,8 +912,8 @@ var GeneralTab = React.createClass({
title='Nickname'
inputs={inputs}
submit={this.submitNickname}
- server_error={server_error}
- client_error={client_error}
+ server_error={serverError}
+ client_error={clientError}
updateSection={function(e) {
self.updateSection('');
e.preventDefault();
@@ -913,7 +936,7 @@ var GeneralTab = React.createClass({
if (this.props.activeSection === 'username') {
inputs.push(
<div className='form-group'>
- <label className='col-sm-5 control-label'>{utils.isMobile() ? '': 'Username'}</label>
+ <label className='col-sm-5 control-label'>{utils.isMobile() ? '' : 'Username'}</label>
<div className='col-sm-7'>
<input className='form-control' type='text' onChange={this.updateUsername} value={this.state.username}/>
</div>
@@ -925,8 +948,8 @@ var GeneralTab = React.createClass({
title='Username'
inputs={inputs}
submit={this.submitUsername}
- server_error={server_error}
- client_error={client_error}
+ server_error={serverError}
+ client_error={clientError}
updateSection={function(e) {
self.updateSection('');
e.preventDefault();
@@ -960,8 +983,8 @@ var GeneralTab = React.createClass({
title='Email'
inputs={inputs}
submit={this.submitEmail}
- server_error={server_error}
- client_error={email_error}
+ server_error={serverError}
+ client_error={emailError}
updateSection={function(e) {
self.updateSection('');
e.preventDefault();
@@ -987,8 +1010,8 @@ var GeneralTab = React.createClass({
title='Profile Picture'
submit={this.submitPicture}
src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update}
- server_error={server_error}
- client_error={client_error}
+ server_error={serverError}
+ client_error={clientError}
updateSection={function(e) {
self.updateSection('');
e.preventDefault();
@@ -996,6 +1019,7 @@ var GeneralTab = React.createClass({
picture={this.state.picture}
pictureChange={this.updatePicture}
submitActive={this.submitActive}
+ loadingPicture={this.state.loadingPicture}
/>
);
} else {
@@ -1063,6 +1087,7 @@ var AppearanceTab = React.createClass({
},
handleClose: function() {
this.setState({server_error: null});
+ this.props.updateTab('general');
},
componentDidMount: function() {
if (this.props.activeSection === "theme") {
@@ -1078,6 +1103,7 @@ var AppearanceTab = React.createClass({
},
componentWillUnmount: function() {
$('#user_settings1').off('hidden.bs.modal', this.handleClose);
+ this.props.updateSection('');
},
getInitialState: function() {
var user = UserStore.getCurrentUser();
@@ -1146,10 +1172,11 @@ var AppearanceTab = React.createClass({
</div>
</div>
);
- }
+ }
});
module.exports = React.createClass({
+ displayName: 'UserSettings',
componentDidMount: function() {
UserStore.addChangeListener(this._onChange);
},
@@ -1175,19 +1202,19 @@ module.exports = React.createClass({
} else if (this.props.activeTab === 'security') {
return (
<div>
- <SecurityTab user={this.state.user} activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
+ <SecurityTab user={this.state.user} activeSection={this.props.activeSection} updateSection={this.props.updateSection} updateTab={this.props.updateTab} />
</div>
);
} else if (this.props.activeTab === 'notifications') {
return (
<div>
- <NotificationsTab user={this.state.user} activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
+ <NotificationsTab user={this.state.user} activeSection={this.props.activeSection} updateSection={this.props.updateSection} updateTab={this.props.updateTab} />
</div>
);
} else if (this.props.activeTab === 'appearance') {
return (
<div>
- <AppearanceTab activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
+ <AppearanceTab activeSection={this.props.activeSection} updateSection={this.props.updateSection} updateTab={this.props.updateTab} />
</div>
);
} else {
diff --git a/web/react/components/user_settings_modal.jsx b/web/react/components/user_settings_modal.jsx
index 421027244..702e7ad7a 100644
--- a/web/react/components/user_settings_modal.jsx
+++ b/web/react/components/user_settings_modal.jsx
@@ -32,7 +32,7 @@ module.exports = React.createClass({
tabs.push({name: "appearance", ui_name: "Appearance", icon: "glyphicon glyphicon-wrench"});
return (
- <div className="modal fade" ref="modal" id="user_settings1" role="dialog" aria-hidden="true">
+ <div className="modal fade" ref="modal" id="user_settings1" role="dialog" tabIndex="-1" aria-hidden="true">
<div className="modal-dialog settings-modal">
<div className="modal-content">
<div className="modal-header">
@@ -53,6 +53,7 @@ module.exports = React.createClass({
activeTab={this.state.active_tab}
activeSection={this.state.active_section}
updateSection={this.updateSection}
+ updateTab={this.updateTab}
/>
</div>
</div>
diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx
index ecf54ede6..ea1e75ecb 100644
--- a/web/react/stores/post_store.jsx
+++ b/web/react/stores/post_store.jsx
@@ -6,7 +6,6 @@ var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var ChannelStore = require('../stores/channel_store.jsx');
-var UserStore = require('../stores/user_store.jsx');
var BrowserStore = require('../stores/browser_store.jsx');
var Constants = require('../utils/constants.jsx');
@@ -21,185 +20,184 @@ var ADD_MENTION_EVENT = 'add_mention';
var PostStore = assign({}, EventEmitter.prototype, {
- emitChange: function() {
- this.emit(CHANGE_EVENT);
- },
-
- addChangeListener: function(callback) {
- this.on(CHANGE_EVENT, callback);
- },
-
- removeChangeListener: function(callback) {
- this.removeListener(CHANGE_EVENT, callback);
- },
-
- emitSearchChange: function() {
- this.emit(SEARCH_CHANGE_EVENT);
- },
-
- addSearchChangeListener: function(callback) {
- this.on(SEARCH_CHANGE_EVENT, callback);
- },
-
- removeSearchChangeListener: function(callback) {
- this.removeListener(SEARCH_CHANGE_EVENT, callback);
- },
-
- emitSearchTermChange: function(doSearch, isMentionSearch) {
- this.emit(SEARCH_TERM_CHANGE_EVENT, doSearch, isMentionSearch);
- },
-
- addSearchTermChangeListener: function(callback) {
- this.on(SEARCH_TERM_CHANGE_EVENT, callback);
- },
-
- removeSearchTermChangeListener: function(callback) {
- this.removeListener(SEARCH_TERM_CHANGE_EVENT, callback);
- },
-
- emitSelectedPostChange: function(from_search) {
- this.emit(SELECTED_POST_CHANGE_EVENT, from_search);
- },
-
- addSelectedPostChangeListener: function(callback) {
- this.on(SELECTED_POST_CHANGE_EVENT, callback);
- },
-
- removeSelectedPostChangeListener: function(callback) {
- this.removeListener(SELECTED_POST_CHANGE_EVENT, callback);
- },
-
- emitMentionDataChange: function(id, mentionText, excludeList) {
- this.emit(MENTION_DATA_CHANGE_EVENT, id, mentionText, excludeList);
- },
-
- addMentionDataChangeListener: function(callback) {
- this.on(MENTION_DATA_CHANGE_EVENT, callback);
- },
-
- removeMentionDataChangeListener: function(callback) {
- this.removeListener(MENTION_DATA_CHANGE_EVENT, callback);
- },
-
- emitAddMention: function(id, username) {
- this.emit(ADD_MENTION_EVENT, id, username);
- },
-
- addAddMentionListener: function(callback) {
- this.on(ADD_MENTION_EVENT, callback);
- },
-
- removeAddMentionListener: function(callback) {
- this.removeListener(ADD_MENTION_EVENT, callback);
- },
-
- getCurrentPosts: function() {
- var currentId = ChannelStore.getCurrentId();
-
- if (currentId != null)
- return this.getPosts(currentId);
- else
- return null;
- },
- storePosts: function(channelId, posts) {
- this._storePosts(channelId, posts);
- this.emitChange();
- },
- _storePosts: function(channelId, posts) {
- BrowserStore.setItem("posts_" + channelId, posts);
- },
- getPosts: function(channelId) {
- return BrowserStore.getItem("posts_" + channelId);
- },
- storeSearchResults: function(results, is_mention_search) {
- BrowserStore.setItem("search_results", results);
- is_mention_search = is_mention_search ? true : false; // force to bool
- BrowserStore.setItem("is_mention_search", is_mention_search);
- },
- getSearchResults: function() {
- return BrowserStore.getItem("search_results");
- },
- getIsMentionSearch: function() {
- return BrowserStore.getItem("is_mention_search");
- },
- storeSelectedPost: function(post_list) {
- BrowserStore.setItem("select_post", post_list);
- },
- getSelectedPost: function() {
- return BrowserStore.getItem("select_post");
- },
- storeSearchTerm: function(term) {
- BrowserStore.setItem("search_term", term);
- },
- getSearchTerm: function() {
- return BrowserStore.getItem("search_term");
- },
- storeCurrentDraft: function(draft) {
- var channel_id = ChannelStore.getCurrentId();
- BrowserStore.setItem("draft_" + channel_id, draft);
- },
- getCurrentDraft: function() {
- var channel_id = ChannelStore.getCurrentId();
- return BrowserStore.getItem("draft_" + channel_id);
- },
- storeDraft: function(channel_id, draft) {
- BrowserStore.setItem("draft_" + channel_id, draft);
- },
- getDraft: function(channel_id) {
- return BrowserStore.getItem("draft_" + channel_id);
- },
- storeCommentDraft: function(parent_post_id, draft) {
- BrowserStore.setItem("comment_draft_" + parent_post_id, draft);
- },
- getCommentDraft: function(parent_post_id) {
- return BrowserStore.getItem("comment_draft_" + parent_post_id);
- },
- clearDraftUploads: function() {
- BrowserStore.actionOnItemsWithPrefix("draft_", function (key, value) {
- if (value) {
- value.uploadsInProgress = 0;
- BrowserStore.setItem(key, value);
- }
- });
- },
- clearCommentDraftUploads: function() {
- BrowserStore.actionOnItemsWithPrefix("comment_draft_", function (key, value) {
- if (value) {
- value.uploadsInProgress = 0;
- BrowserStore.setItem(key, value);
- }
- });
- }
+ emitChange: function emitChange() {
+ this.emit(CHANGE_EVENT);
+ },
+
+ addChangeListener: function addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ },
+
+ removeChangeListener: function removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ },
+
+ emitSearchChange: function emitSearchChange() {
+ this.emit(SEARCH_CHANGE_EVENT);
+ },
+
+ addSearchChangeListener: function addSearchChangeListener(callback) {
+ this.on(SEARCH_CHANGE_EVENT, callback);
+ },
+
+ removeSearchChangeListener: function removeSearchChangeListener(callback) {
+ this.removeListener(SEARCH_CHANGE_EVENT, callback);
+ },
+
+ emitSearchTermChange: function emitSearchTermChange(doSearch, isMentionSearch) {
+ this.emit(SEARCH_TERM_CHANGE_EVENT, doSearch, isMentionSearch);
+ },
+
+ addSearchTermChangeListener: function addSearchTermChangeListener(callback) {
+ this.on(SEARCH_TERM_CHANGE_EVENT, callback);
+ },
+
+ removeSearchTermChangeListener: function removeSearchTermChangeListener(callback) {
+ this.removeListener(SEARCH_TERM_CHANGE_EVENT, callback);
+ },
+
+ emitSelectedPostChange: function emitSelectedPostChange(fromSearch) {
+ this.emit(SELECTED_POST_CHANGE_EVENT, fromSearch);
+ },
+
+ addSelectedPostChangeListener: function addSelectedPostChangeListener(callback) {
+ this.on(SELECTED_POST_CHANGE_EVENT, callback);
+ },
+
+ removeSelectedPostChangeListener: function removeSelectedPostChangeListener(callback) {
+ this.removeListener(SELECTED_POST_CHANGE_EVENT, callback);
+ },
+
+ emitMentionDataChange: function emitMentionDataChange(id, mentionText) {
+ this.emit(MENTION_DATA_CHANGE_EVENT, id, mentionText);
+ },
+
+ addMentionDataChangeListener: function addMentionDataChangeListener(callback) {
+ this.on(MENTION_DATA_CHANGE_EVENT, callback);
+ },
+
+ removeMentionDataChangeListener: function removeMentionDataChangeListener(callback) {
+ this.removeListener(MENTION_DATA_CHANGE_EVENT, callback);
+ },
+
+ emitAddMention: function emitAddMention(id, username) {
+ this.emit(ADD_MENTION_EVENT, id, username);
+ },
+
+ addAddMentionListener: function addAddMentionListener(callback) {
+ this.on(ADD_MENTION_EVENT, callback);
+ },
+
+ removeAddMentionListener: function removeAddMentionListener(callback) {
+ this.removeListener(ADD_MENTION_EVENT, callback);
+ },
+
+ getCurrentPosts: function getCurrentPosts() {
+ var currentId = ChannelStore.getCurrentId();
+
+ if (currentId != null) {
+ return this.getPosts(currentId);
+ }
+ return null;
+ },
+ storePosts: function storePosts(channelId, posts) {
+ this.pStorePosts(channelId, posts);
+ this.emitChange();
+ },
+ pStorePosts: function pStorePosts(channelId, posts) {
+ BrowserStore.setItem('posts_' + channelId, posts);
+ },
+ getPosts: function getPosts(channelId) {
+ return BrowserStore.getItem('posts_' + channelId);
+ },
+ storeSearchResults: function storeSearchResults(results, isMentionSearch) {
+ BrowserStore.setItem('search_results', results);
+ BrowserStore.setItem('is_mention_search', Boolean(isMentionSearch));
+ },
+ getSearchResults: function getSearchResults() {
+ return BrowserStore.getItem('search_results');
+ },
+ getIsMentionSearch: function getIsMentionSearch() {
+ return BrowserStore.getItem('is_mention_search');
+ },
+ storeSelectedPost: function storeSelectedPost(postList) {
+ BrowserStore.setItem('select_post', postList);
+ },
+ getSelectedPost: function getSelectedPost() {
+ return BrowserStore.getItem('select_post');
+ },
+ storeSearchTerm: function storeSearchTerm(term) {
+ BrowserStore.setItem('search_term', term);
+ },
+ getSearchTerm: function getSearchTerm() {
+ return BrowserStore.getItem('search_term');
+ },
+ storeCurrentDraft: function storeCurrentDraft(draft) {
+ var channelId = ChannelStore.getCurrentId();
+ BrowserStore.setItem('draft_' + channelId, draft);
+ },
+ getCurrentDraft: function getCurrentDraft() {
+ var channelId = ChannelStore.getCurrentId();
+ return BrowserStore.getItem('draft_' + channelId);
+ },
+ storeDraft: function storeDraft(channelId, draft) {
+ BrowserStore.setItem('draft_' + channelId, draft);
+ },
+ getDraft: function getDraft(channelId) {
+ return BrowserStore.getItem('draft_' + channelId);
+ },
+ storeCommentDraft: function storeCommentDraft(parentPostId, draft) {
+ BrowserStore.setItem('comment_draft_' + parentPostId, draft);
+ },
+ getCommentDraft: function getCommentDraft(parentPostId) {
+ return BrowserStore.getItem('comment_draft_' + parentPostId);
+ },
+ clearDraftUploads: function clearDraftUploads() {
+ BrowserStore.actionOnItemsWithPrefix('draft_', function clearUploads(key, value) {
+ if (value) {
+ value.uploadsInProgress = 0;
+ BrowserStore.setItem(key, value);
+ }
+ });
+ },
+ clearCommentDraftUploads: function clearCommentDraftUploads() {
+ BrowserStore.actionOnItemsWithPrefix('comment_draft_', function clearUploads(key, value) {
+ if (value) {
+ value.uploadsInProgress = 0;
+ BrowserStore.setItem(key, value);
+ }
+ });
+ }
});
-PostStore.dispatchToken = AppDispatcher.register(function(payload) {
- var action = payload.action;
-
- switch(action.type) {
- case ActionTypes.RECIEVED_POSTS:
- PostStore._storePosts(action.id, action.post_list);
- PostStore.emitChange();
- break;
- case ActionTypes.RECIEVED_SEARCH:
- PostStore.storeSearchResults(action.results, action.is_mention_search);
- PostStore.emitSearchChange();
- break;
- case ActionTypes.RECIEVED_SEARCH_TERM:
- PostStore.storeSearchTerm(action.term);
- PostStore.emitSearchTermChange(action.do_search, action.is_mention_search);
- break;
- case ActionTypes.RECIEVED_POST_SELECTED:
- PostStore.storeSelectedPost(action.post_list);
- PostStore.emitSelectedPostChange(action.from_search);
- break;
- case ActionTypes.RECIEVED_MENTION_DATA:
- PostStore.emitMentionDataChange(action.id, action.mention_text, action.exclude_list);
- break;
- case ActionTypes.RECIEVED_ADD_MENTION:
- PostStore.emitAddMention(action.id, action.username);
- break;
- default:
- }
+PostStore.dispatchToken = AppDispatcher.register(function registry(payload) {
+ var action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECIEVED_POSTS:
+ PostStore.pStorePosts(action.id, action.post_list);
+ PostStore.emitChange();
+ break;
+ case ActionTypes.RECIEVED_SEARCH:
+ PostStore.storeSearchResults(action.results, action.is_mention_search);
+ PostStore.emitSearchChange();
+ break;
+ case ActionTypes.RECIEVED_SEARCH_TERM:
+ PostStore.storeSearchTerm(action.term);
+ PostStore.emitSearchTermChange(action.do_search, action.is_mention_search);
+ break;
+ case ActionTypes.RECIEVED_POST_SELECTED:
+ PostStore.storeSelectedPost(action.post_list);
+ PostStore.emitSelectedPostChange(action.from_search);
+ break;
+ case ActionTypes.RECIEVED_MENTION_DATA:
+ PostStore.emitMentionDataChange(action.id, action.mention_text);
+ break;
+ case ActionTypes.RECIEVED_ADD_MENTION:
+ PostStore.emitAddMention(action.id, action.username);
+ break;
+ default:
+ }
});
module.exports = PostStore;
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 2b0976afd..bed0ec556 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -82,7 +82,7 @@ module.exports = {
"channel",
],
MONTHS: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
- MAX_DMS: 10,
+ MAX_DMS: 20,
ONLINE_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns#' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' sodipodi:docname='TRASH_1_4.svg' inkscape:version='0.48.4 r9939' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='12px' height='12px' viewBox='0 0 12 12' enable-background='new 0 0 12 12' xml:space='preserve'><sodipodi:namedview inkscape:cy='139.7898' inkscape:cx='26.358185' inkscape:zoom='1.18' showguides='true' showgrid='false' id='namedview6' guidetolerance='10' gridtolerance='10' objecttolerance='10' borderopacity='1' bordercolor='#666666' pagecolor='#ffffff' inkscape:current-layer='Layer_1' inkscape:window-maximized='1' inkscape:window-y='-8' inkscape:window-x='-8' inkscape:window-height='705' inkscape:window-width='1366' inkscape:guide-bbox='true' inkscape:pageshadow='2' inkscape:pageopacity='0'><sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide><sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide></sodipodi:namedview><g><g><path class='online--icon' d='M6,5.487c1.371,0,2.482-1.116,2.482-2.493c0-1.378-1.111-2.495-2.482-2.495S3.518,1.616,3.518,2.994C3.518,4.371,4.629,5.487,6,5.487z M10.452,8.545c-0.101-0.829-0.36-1.968-0.726-2.541C9.475,5.606,8.5,5.5,8.5,5.5S8.43,7.521,6,7.521C3.507,7.521,3.5,5.5,3.5,5.5S2.527,5.606,2.273,6.004C1.908,6.577,1.648,7.716,1.547,8.545C1.521,8.688,1.49,9.082,1.498,9.142c0.161,1.295,2.238,2.322,4.375,2.358C5.916,11.501,5.958,11.501,6,11.501c0.043,0,0.084,0,0.127-0.001c2.076-0.026,4.214-1.063,4.375-2.358C10.509,9.082,10.471,8.696,10.452,8.545z'/></g></g></svg>",
OFFLINE_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns#' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' sodipodi:docname='TRASH_1_4.svg' inkscape:version='0.48.4 r9939' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='12px' height='12px' viewBox='0 0 12 12' enable-background='new 0 0 12 12' xml:space='preserve'><sodipodi:namedview inkscape:cy='139.7898' inkscape:cx='26.358185' inkscape:zoom='1.18' showguides='true' showgrid='false' id='namedview6' guidetolerance='10' gridtolerance='10' objecttolerance='10' borderopacity='1' bordercolor='#666666' pagecolor='#ffffff' inkscape:current-layer='Layer_1' inkscape:window-maximized='1' inkscape:window-y='-8' inkscape:window-x='-8' inkscape:window-height='705' inkscape:window-width='1366' inkscape:guide-bbox='true' inkscape:pageshadow='2' inkscape:pageopacity='0'><sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide><sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide></sodipodi:namedview><g><g><path fill='#cccccc' d='M6.002,7.143C5.645,7.363,5.167,7.52,4.502,7.52c-2.493,0-2.5-2.02-2.5-2.02S1.029,5.607,0.775,6.004C0.41,6.577,0.15,7.716,0.049,8.545c-0.025,0.145-0.057,0.537-0.05,0.598c0.162,1.295,2.237,2.321,4.375,2.357c0.043,0.001,0.085,0.001,0.127,0.001c0.043,0,0.084,0,0.127-0.001c1.879-0.023,3.793-0.879,4.263-2h-2.89L6.002,7.143L6.002,7.143z M4.501,5.488c1.372,0,2.483-1.117,2.483-2.494c0-1.378-1.111-2.495-2.483-2.495c-1.371,0-2.481,1.117-2.481,2.495C2.02,4.371,3.13,5.488,4.501,5.488z M7.002,6.5v2h5v-2H7.002z'/></g></g></svg>",
MENU_ICON: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>",
diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss
index 52659521d..78006ff18 100644
--- a/web/sass-files/sass/partials/_base.scss
+++ b/web/sass-files/sass/partials/_base.scss
@@ -49,6 +49,12 @@ div.theme {
background-color: $primary-color;
}
+.tooltip {
+ .tooltip-inner {
+ word-break: break-word;
+ }
+}
+
.nopadding {
padding: 0;
margin: 0;
@@ -61,6 +67,10 @@ div.theme {
}
}
+.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control {
+ cursor: auto;
+}
+
.form-group {
&.form-group--small {
margin-bottom: 10px;
diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss
index 22e2f44c5..65775f01e 100644
--- a/web/sass-files/sass/partials/_files.scss
+++ b/web/sass-files/sass/partials/_files.scss
@@ -4,7 +4,8 @@
max-height: 110px;
height: 110px;
white-space: nowrap;
- overflow: auto;
+ overflow-x: auto;
+ overflow-y: hidden;
.preview-div {
display: inline-block;
width: 120px;
@@ -28,9 +29,9 @@
}
}
.preview-img {
- display: block;
- height: auto;
- max-width: 100%;
+ display: block;
+ height: auto;
+ max-width: 100%;
}
.remove-preview {
position: absolute;
@@ -129,10 +130,10 @@
background-color: #FFF;
background-repeat: no-repeat;
&.small {
- background-position: center;
+ background-position: center;
}
&.normal {
- background-position: top left;
+ background-position: top left;
}
}
.post-image__thumbnail {
@@ -140,6 +141,8 @@
vertical-align: top;
width: 50%;
height: 100%;
+ cursor: zoom-in;
+ cursor: -webkit-zoom-in;
}
.post-image__details {
display: table-cell;
@@ -168,34 +171,34 @@
}
.file-details__container {
- @include display-flex;
- display: -ms-flexbox;
+ @include display-flex;
+ display: -ms-flexbox;
- .file-details {
- width: 320px;
- height: 270px;
- padding: 14px;
- text-align: left;
- vertical-align: top;
+ .file-details {
+ width: 320px;
+ height: 270px;
+ padding: 14px;
+ text-align: left;
+ vertical-align: top;
- .file-details__name {
- font-size: 16px;
- }
- .file-details__info {
- color: grey;
- }
+ .file-details__name {
+ font-size: 16px;
}
- .file-details__preview {
- width: 320px;
- height: 270px;
- border-right: 1px solid #ddd;
- vertical-align: center;
+ .file-details__info {
+ color: grey;
+ }
+ }
+ .file-details__preview {
+ width: 320px;
+ height: 270px;
+ border-right: 1px solid #ddd;
+ vertical-align: center;
// helper to center the image icon in the preview window
.file-details__preview-helper {
- height: 100%;
- display: inline-block;
- vertical-align: middle;
+ height: 100%;
+ display: inline-block;
+ vertical-align: middle;
}
+ }
}
-}
diff --git a/web/sass-files/sass/partials/_get-link.scss b/web/sass-files/sass/partials/_get-link.scss
new file mode 100644
index 000000000..c84befd6a
--- /dev/null
+++ b/web/sass-files/sass/partials/_get-link.scss
@@ -0,0 +1,6 @@
+.copy-link-confirm {
+ position: fixed;
+ color: rgb(153, 230, 153);
+ top: 84%;
+ left: 130px;
+} \ No newline at end of file
diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss
index fb37c43eb..da648a170 100644
--- a/web/sass-files/sass/partials/_headers.scss
+++ b/web/sass-files/sass/partials/_headers.scss
@@ -110,6 +110,20 @@
}
}
}
+ &.theme--black {
+ &:hover {
+ &:before {
+ background: rgba(white, 0.2);
+ }
+ }
+ }
+ &.theme--gray {
+ &:hover {
+ &:before {
+ background: rgba(white, 0.1);
+ }
+ }
+ }
a {
color: #fff;
}
diff --git a/web/sass-files/sass/partials/_modal.scss b/web/sass-files/sass/partials/_modal.scss
index f359037c5..014f834ed 100644
--- a/web/sass-files/sass/partials/_modal.scss
+++ b/web/sass-files/sass/partials/_modal.scss
@@ -15,10 +15,13 @@
}
.remove__member {
float: right;
- color: #E56565;
+ color: #999;
font-size: 20px;
line-height: 0;
padding: 6px;
+ &:hover {
+ color: #E56565;
+ }
}
.modal-dialog {
max-width: 95%;
@@ -151,10 +154,9 @@
height: 100%;
margin: 0 auto;
.image-wrapper {
- background: #FFF;
position: relative;
max-width: 90%;
- min-height: 50px;
+ min-height: 100px;
min-width: 320px;
@include border-radius(3px);
display: table;
@@ -182,6 +184,7 @@
z-index: 9999;
}
> a {
+ background: #FFF;
display: table-cell;
vertical-align: middle;
}
diff --git a/web/sass-files/sass/partials/_navbar.scss b/web/sass-files/sass/partials/_navbar.scss
index 905907d84..2e78a8728 100644
--- a/web/sass-files/sass/partials/_navbar.scss
+++ b/web/sass-files/sass/partials/_navbar.scss
@@ -19,6 +19,7 @@
}
}
.navbar-toggle {
+ width: 43px;
float: left;
border-color: transparent;
border-radius: 0;
diff --git a/web/sass-files/sass/partials/_popover.scss b/web/sass-files/sass/partials/_popover.scss
index fa1b44841..5008331b4 100644
--- a/web/sass-files/sass/partials/_popover.scss
+++ b/web/sass-files/sass/partials/_popover.scss
@@ -6,4 +6,11 @@
.user-popover__image {
margin: 0 0 10px;
@include border-radius(128px);
+}
+
+.user-popover__email {
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: block;
} \ No newline at end of file
diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss
index e3f140413..47b2b6bd7 100644
--- a/web/sass-files/sass/partials/_responsive.scss
+++ b/web/sass-files/sass/partials/_responsive.scss
@@ -229,6 +229,16 @@
}
}
+@media screen and (max-height: 640px) {
+ .signup-team__container {
+ padding: 30px 0;
+ margin-bottom: 30px;
+ font-size: 0.9em;
+ .signup-team__name {
+ font-size: 2em;
+ }
+ }
+}
@media screen and (max-width: 768px) {
.date-separator, .new-separator {
&.hovered--after {
diff --git a/web/sass-files/sass/styles.scss b/web/sass-files/sass/styles.scss
index ffd1f42b8..eb5152a2c 100644
--- a/web/sass-files/sass/styles.scss
+++ b/web/sass-files/sass/styles.scss
@@ -33,6 +33,7 @@
@import "partials/error";
@import "partials/error-bar";
@import "partials/loading";
+@import "partials/get-link";
// Responsive Css
@import "partials/responsive";
diff --git a/web/static/config/config.js b/web/static/config/config.js
index 0d564b77e..00cae7ab2 100644
--- a/web/static/config/config.js
+++ b/web/static/config/config.js
@@ -31,6 +31,9 @@ var config = {
ReportProblemLink: "/static/help/configure_links.html",
HomeLink: "",
+ // Toggle whether or not users are shown a message about agreeing to the Terms of Service during the signup process
+ ShowTermsDuringSignup: false,
+
ThemeColors: ["#2389d7", "#008a17", "#dc4fad", "#ac193d", "#0072c6", "#d24726", "#ff8f32", "#82ba00", "#03b3b2", "#008299", "#4617b4", "#8c0095", "#004b8b", "#004b8b", "#570000", "#380000", "#585858", "#000000"]
};
diff --git a/web/templates/channel.html b/web/templates/channel.html
index 6325069ee..da6fed97d 100644
--- a/web/templates/channel.html
+++ b/web/templates/channel.html
@@ -49,7 +49,9 @@
<div id="activity_log_modal"></div>
<div id="removed_from_channel_modal"></div>
<script>
-window.setup_channel_page('{{ .Props.TeamDisplayName }}', '{{ .Props.TeamType }}', '{{ .Props.TeamId }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}');
+ window.setup_channel_page('{{ .Props.TeamDisplayName }}', '{{ .Props.TeamType }}', '{{ .Props.TeamId }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}');
+ $('body').tooltip( {selector: '[data-toggle=tooltip]'} );
+ $('.modal-body').perfectScrollbar();
</script>
</body>
</html>