summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/react/components/channel_invite_modal.jsx127
-rw-r--r--web/react/components/member_list_item.jsx32
-rw-r--r--web/react/components/more_channels.jsx54
-rw-r--r--web/react/components/post_list.jsx13
-rw-r--r--web/react/components/post_right.jsx3
-rw-r--r--web/react/components/sidebar.jsx4
-rw-r--r--web/react/stores/channel_store.jsx2
-rw-r--r--web/react/utils/async_client.jsx2
-rw-r--r--web/sass-files/sass/partials/_files.scss2
-rw-r--r--web/sass-files/sass/partials/_loading.scss8
-rw-r--r--web/sass-files/sass/partials/_post.scss116
-rw-r--r--web/sass-files/sass/partials/_responsive.scss32
12 files changed, 208 insertions, 187 deletions
diff --git a/web/react/components/channel_invite_modal.jsx b/web/react/components/channel_invite_modal.jsx
index d41453fab..1b8fe4199 100644
--- a/web/react/components/channel_invite_modal.jsx
+++ b/web/react/components/channel_invite_modal.jsx
@@ -10,111 +10,95 @@ var AsyncClient = require('../utils/async_client.jsx');
function getStateFromStores() {
var users = UserStore.getActiveOnlyProfiles();
- var member_list = ChannelStore.getCurrentExtraInfo().members;
+ var memberIds = ChannelStore.getCurrentExtraInfo().members.map(function(user) { return user.id; });
- var nonmember_list = [];
+ var nonmembers = [];
for (var id in users) {
- var found = false;
- for (var i = 0; i < member_list.length; i++) {
- if (member_list[i].id === id) {
- found = true;
- break;
- }
- }
- if (!found) {
- nonmember_list.push(users[id]);
+ if (memberIds.indexOf(id) == -1) {
+ nonmembers.push(users[id]);
}
}
- member_list.sort(function(a,b) {
- if (a.username < b.username) return -1;
- if (a.username > b.username) return 1;
- return 0;
- });
-
- nonmember_list.sort(function(a,b) {
- if (a.username < b.username) return -1;
- if (a.username > b.username) return 1;
- return 0;
+ nonmembers.sort(function(a,b) {
+ return a.username.localeCompare(b.username);
});
var channel_name = ChannelStore.getCurrent() ? ChannelStore.getCurrent().display_name : "";
return {
- nonmember_list: nonmember_list,
- member_list: member_list,
+ nonmembers: nonmembers,
+ memberIds: memberIds,
channel_name: channel_name
};
}
module.exports = React.createClass({
+ displayName: "ChannelInviteModal",
+
+ isShown: false,
+ getInitialState: function() {
+ return {};
+ },
+
componentDidMount: function() {
+ $(React.findDOMNode(this))
+ .on('hidden.bs.modal', this._onHide)
+ .on('show.bs.modal', this._onShow);
+ },
+
+ _onShow: function() {
ChannelStore.addExtraInfoChangeListener(this._onChange);
ChannelStore.addChangeListener(this._onChange);
-
- var self = this;
- $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) {
- self.setState({ render_members: false });
- });
-
- $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
- self.setState({ render_members: true });
- });
+ this.isShown = true;
+ this._onChange();
},
- componentWillUnmount: function() {
+
+ _onHide: function() {
ChannelStore.removeExtraInfoChangeListener(this._onChange);
ChannelStore.removeChangeListener(this._onChange);
+ this.isShown = false;
},
+
_onChange: function() {
- var new_state = getStateFromStores();
- if (!utils.areStatesEqual(this.state, new_state)) {
- this.setState(new_state);
- }
+ this.setState(getStateFromStores());
},
+
handleInvite: function(user_id) {
// Make sure the user isn't already a member of the channel
- var member_list = this.state.member_list;
- for (var i = 0; i < member_list; i++) {
- if (member_list[i].id === user_id) {
- return;
- }
+ if (this.state.memberIds.indexOf(user_id) > -1) {
+ return;
}
var data = {};
- data['user_id'] = user_id;
+ data.user_id = user_id;
client.addChannelMember(ChannelStore.getCurrentId(), data,
- function(data) {
- var nonmember_list = this.state.nonmember_list;
- var new_member;
- for (var i = 0; i < nonmember_list.length; i++) {
- if (user_id === nonmember_list[i].id) {
- nonmember_list[i].invited = true;
- new_member = nonmember_list[i];
+ function() {
+ var nonmembers = this.state.nonmembers;
+ var memberIds = this.state.memberIds;
+
+ for (var i = 0; i < nonmembers.length; i++) {
+ if (user_id === nonmembers[i].id) {
+ nonmembers[i].invited = true;
+ memberIds.push(user_id);
break;
}
}
- if (new_member) {
- member_list.push(new_member);
- member_list.sort(function(a,b) {
- if (a.username < b.username) return -1;
- if (a.username > b.username) return 1;
- return 0;
- });
- }
-
- this.setState({ invite_error: null, member_list: member_list, nonmember_list: nonmember_list });
+ this.setState({ invite_error: null, memberIds: memberIds, nonmembers: nonmembers });
AsyncClient.getChannelExtraInfo(true);
}.bind(this),
+
function(err) {
this.setState({ invite_error: err.message });
}.bind(this)
);
},
- getInitialState: function() {
- return getStateFromStores();
+
+ shouldComponentUpdate: function(nextProps, nextState) {
+ return this.isShown && !utils.areStatesEqual(this.state, nextState);
},
+
render: function() {
var invite_error = this.state.invite_error ? <label className='has-error control-label'>{this.state.invite_error}</label> : null;
@@ -125,22 +109,19 @@ module.exports = React.createClass({
}
return (
- <div className="modal fade" ref="modal" id="channel_invite" role="dialog" aria-hidden="true">
- <div className="modal-dialog">
+ <div className="modal fade" id="channel_invite" tabIndex="-1" role="dialog" aria-hidden="true">
+ <div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
- <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+ <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 className="modal-title">Add New Members to {this.state.channel_name}</h4>
</div>
<div className="modal-body">
{ invite_error }
- { this.state.render_members ?
<MemberList
- memberList={this.state.nonmember_list}
- isAdmin={isAdmin}
- handleInvite={this.handleInvite}
- />
- : "" }
+ memberList={this.state.nonmembers}
+ isAdmin={isAdmin}
+ handleInvite={this.handleInvite} />
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
@@ -151,7 +132,3 @@ module.exports = React.createClass({
);
}
});
-
-
-
-
diff --git a/web/react/components/member_list_item.jsx b/web/react/components/member_list_item.jsx
index f0bbff8bd..357fd49a8 100644
--- a/web/react/components/member_list_item.jsx
+++ b/web/react/components/member_list_item.jsx
@@ -5,13 +5,17 @@ var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
module.exports = React.createClass({
- handleInvite: function() {
+ displayName: 'MemberListItem',
+ handleInvite: function(e) {
+ e.preventDefault();
this.props.handleInvite(this.props.member.id);
},
- handleRemove: function() {
+ handleRemove: function(e) {
+ e.preventDefault();
this.props.handleRemove(this.props.member.id);
},
- handleMakeAdmin: function() {
+ handleMakeAdmin: function(e) {
+ e.preventDefault();
this.props.handleMakeAdmin(this.props.member.id);
},
render: function() {
@@ -20,12 +24,6 @@ module.exports = React.createClass({
var isAdmin = this.props.isAdmin;
var isMemberAdmin = member.roles.indexOf("admin") > -1;
- if (member.roles === '') {
- member.roles = 'Member';
- } else {
- member.roles = member.roles.charAt(0).toUpperCase() + member.roles.slice(1);
- }
-
var invite;
if (member.invited && this.props.handleInvite) {
invite = <span className="member-role">Added</span>;
@@ -36,30 +34,28 @@ module.exports = React.createClass({
invite = (
<div className="dropdown member-drop">
<a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
- <span>{member.roles} </span>
+ <span className="text-capitalize">{member.roles || 'Member'} </span>
<span className="caret"></span>
</a>
<ul className="dropdown-menu member-menu" role="menu" aria-labelledby="channel_header_dropdown">
{ this.props.handleMakeAdmin ?
- <li role="presentation"><a role="menuitem" onClick={self.handleMakeAdmin}>Make Admin</a></li>
- : "" }
+ <li role="presentation"><a href="" role="menuitem" onClick={self.handleMakeAdmin}>Make Admin</a></li>
+ : null }
{ this.props.handleRemove ?
- <li role="presentation"><a role="menuitem" onClick={self.handleRemove}>Remove Member</a></li>
- : "" }
+ <li role="presentation"><a href="" role="menuitem" onClick={self.handleRemove}>Remove Member</a></li>
+ : null }
</ul>
</div>
);
} else {
- invite = <div className="member-drop"><span>{member.roles} </span><span className="caret invisible"></span></div>;
+ invite = <div className="member-role text-capitalize" style={{marginRight: 15}}>{member.roles || 'Member'}</div>;
}
- var email = member.email.length > 0 ? member.email : "";
-
return (
<div className="row member-div">
<img className="post-profile-img pull-left" src={"/api/v1/users/" + member.id + "/image"} height="36" width="36" />
<span className="member-name">{member.username}</span>
- <span className="member-email">{email}</span>
+ <span className="member-email">{member.email}</span>
{ invite }
</div>
);
diff --git a/web/react/components/more_channels.jsx b/web/react/components/more_channels.jsx
index be2a5e93c..1af259853 100644
--- a/web/react/components/more_channels.jsx
+++ b/web/react/components/more_channels.jsx
@@ -61,6 +61,10 @@ module.exports = React.createClass({
render: function() {
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 outter = this;
+ var moreChannels;
+
+ if (this.state.channels != null)
+ moreChannels = this.state.channels;
return (
<div className="modal fade" id="more_channels" ref="modal" tabIndex="-1" role="dialog" aria-hidden="true">
@@ -75,26 +79,36 @@ module.exports = React.createClass({
<button data-toggle="modal" data-target="#new_channel" data-channeltype={this.state.channel_type} type="button" className="btn btn-primary channel-create-btn" onClick={this.handleNewChannel}>Create New Channel</button>
</div>
<div className="modal-body">
- {this.state.channels.length ?
- <table className="more-channel-table table">
- <tbody>
- {this.state.channels.map(function(channel) {
- return (
- <tr key={channel.id}>
- <td>
- <p className="more-channel-name">{channel.display_name}</p>
- <p className="more-channel-description">{channel.description}</p>
- </td>
- <td className="td--action"><button onClick={outter.handleJoin.bind(outter, channel.id)} className="pull-right btn btn-primary">Join</button></td>
- </tr>
- )
- })}
- </tbody>
- </table>
- : <div className="no-channel-message">
- <p className="primary-message">No more channels to join</p>
- <p className="secondary-message">Click 'Create New Channel' to make a new one</p>
- </div>}
+ {moreChannels ?
+ (moreChannels.length ?
+ <table className="more-channel-table table">
+ <tbody>
+ {moreChannels.map(function(channel) {
+ return (
+ <tr key={channel.id}>
+ <td>
+ <p className="more-channel-name">{channel.display_name}</p>
+ <p className="more-channel-description">{channel.description}</p>
+ </td>
+ <td className="td--action"><button onClick={outter.handleJoin.bind(outter, channel.id)} className="pull-right btn btn-primary">Join</button></td>
+ </tr>
+ )
+ })}
+ </tbody>
+ </table>
+ : <div className="no-channel-message">
+ <p className="primary-message">No more channels to join</p>
+ <p className="secondary-message">Click 'Create New Channel' to make a new one</p>
+ </div>)
+ : <div ref="loadingscreen" className="loading-screen loading-screen--channel">
+ <div className="loading__content">
+ <h3>Loading</h3>
+ <div id="round_1" className="round"></div>
+ <div id="round_2" className="round"></div>
+ <div id="round_3" className="round"></div>
+ </div>
+ </div>
+ }
{ server_error }
</div>
<div className="modal-footer">
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
index fc5157ce6..d6dc9ce30 100644
--- a/web/react/components/post_list.jsx
+++ b/web/react/components/post_list.jsx
@@ -123,7 +123,7 @@ module.exports = React.createClass({
$('.post-list__content div .post').removeClass('post--last');
$('.post-list__content div:last-child .post').addClass('post--last');
- $('body').on('mouseenter mouseleave', '.post:not(.post--comment.same--root)', function(ev){
+ $('body').on('mouseenter mouseleave', '.post', function(ev){
if(ev.type === 'mouseenter'){
$(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--after');
$(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--before');
@@ -134,6 +134,17 @@ module.exports = React.createClass({
}
});
+ $('body').on('mouseenter mouseleave', '.post.post--comment.same--root', function(ev){
+ if(ev.type === 'mouseenter'){
+ $(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--comment');
+ $(this).parent('div').next('.date-separator, .new-separator').addClass('hovered--comment');
+ }
+ else {
+ $(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--comment');
+ $(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--comment');
+ }
+ });
+
},
componentDidUpdate: function() {
this.resize();
diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx
index 2c28c5d9f..115ee87d4 100644
--- a/web/react/components/post_right.jsx
+++ b/web/react/components/post_right.jsx
@@ -282,7 +282,6 @@ module.exports = React.createClass({
componentDidMount: function() {
PostStore.addSelectedPostChangeListener(this._onChange);
PostStore.addChangeListener(this._onChangeAll);
- $(".post-right__scroll").perfectScrollbar();
this.resize();
var self = this;
$(window).resize(function(){
@@ -341,7 +340,7 @@ module.exports = React.createClass({
var height = $(window).height() - $('#error_bar').outerHeight() - 100;
$(".post-right__scroll").css("height", height + "px");
$(".post-right__scroll").scrollTop(100000);
- $(".post-right__scroll").perfectScrollbar('update');
+ $(".post-right__scroll").perfectScrollbar();
},
render: function() {
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index 0e4d38fe0..2095978e8 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -263,6 +263,10 @@ var SidebarLoggedIn = React.createClass({
if (ChannelStore.getCurrentId() != msg.channel_id) {
AsyncClient.getChannels(true);
}
+ } else if (msg.action == "user_added") {
+ if (UserStore.getCurrentId() === msg.user_id) {
+ AsyncClient.getChannels(true);
+ }
}
},
updateTitle: function() {
diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx
index 77ab69ef8..340ce9922 100644
--- a/web/react/stores/channel_store.jsx
+++ b/web/react/stores/channel_store.jsx
@@ -202,7 +202,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
BrowserStore.setItem("more_channels", JSON.stringify(channels));
},
_getMoreChannels: function() {
- var channels = [];
+ var channels;
try {
channels = JSON.parse(BrowserStore.getItem("more_channels"));
}
diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx
index 9383057c3..a2a6f8db7 100644
--- a/web/react/utils/async_client.jsx
+++ b/web/react/utils/async_client.jsx
@@ -104,7 +104,7 @@ module.exports.updateLastViewedAt = function() {
module.exports.getMoreChannels = function(force) {
if (isCallInProgress("getMoreChannels")) return;
- if (ChannelStore.getMoreAll().length == 0 || force) {
+ if (!ChannelStore.getMoreAll() || force) {
callTracker["getMoreChannels"] = utils.getTimestamp();
client.getMoreChannels(
diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss
index 1268d8a07..79142176e 100644
--- a/web/sass-files/sass/partials/_files.scss
+++ b/web/sass-files/sass/partials/_files.scss
@@ -119,7 +119,7 @@
width: 120px;
height: 100px;
float: left;
- margin: 0 1em 1em 0;
+ margin: 5px 10px 5px 0;
&.custom-file {
width: 85px;
height: 100px;
diff --git a/web/sass-files/sass/partials/_loading.scss b/web/sass-files/sass/partials/_loading.scss
index 185a42180..bc819e8f5 100644
--- a/web/sass-files/sass/partials/_loading.scss
+++ b/web/sass-files/sass/partials/_loading.scss
@@ -5,10 +5,16 @@
position: absolute;
@include box-sizing(border-box);
text-align: center;
+ &.loading-screen--channel {
+ position: relative;
+ padding: 4em 0 3.5em;
+ }
.loading__content {
display: table-cell;
vertical-align: middle;
+ font-size: 0;
h3 {
+ font-size: 16px;
font-weight: 400;
margin: 0 0.2em 0;
display: inline-block;
@@ -23,7 +29,7 @@
width: 4px;
height: 4px;
display: inline-block;
- margin: 0 1px;
+ margin: 0 2px;
opacity: 0.1;
@include border-radius(10px);
-moz-animation: move 0.75s infinite linear;
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index f33cedd16..2d5cd67db 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -41,67 +41,68 @@ body.ios {
min-height:37px;
}
-#post-list {
- .date-separator, .new-separator {
- text-align: center;
- height: 2em;
- margin: 0;
- position: relative;
- &:before, &:after {
- content: "";
- height: 1em;
- position: absolute;
- left: 0;
- width: 100%;
- display: none;
- }
+.date-separator, .new-separator {
+ text-align: center;
+ height: 2em;
+ margin: 0;
+ position: relative;
+ &:before, &:after {
+ content: "";
+ height: 1em;
+ position: absolute;
+ left: 0;
+ width: 100%;
+ display: none;
+ }
+ &:before {
+ bottom: 0;
+ }
+ &:after {
+ top: 0;
+ }
+ &.hovered--after {
&:before {
- bottom: 0;
+ background: #f5f5f5;
+ display: block;
}
+ }
+ &.hovered--before {
&:after {
- top: 0;
- }
- &.hovered--after {
- &:before {
- background: #f5f5f5;
- display: block;
- }
- }
- &.hovered--before {
- &:after {
- background: #f5f5f5;
- display: block;
- }
- }
- .separator__hr {
- border-color: #ccc;
- margin: 0;
- position: relative;
- z-index: 5;
- top: 1em;
- }
- .separator__text {
- line-height: 2em;
- color: #555;
- background: #FFF;
- display: inline-block;
- padding: 0 1em;
- font-weight: 700;
- @include border-radius(50px);
- position: relative;
- z-index: 5;
- font-size: 13px;
+ background: #f5f5f5;
+ display: block;
}
}
- .new-separator {
- .separator__hr {
- border-color: #FFAF53;
- }
- .separator__text {
- color: #F80;
- font-weight: normal;
- }
+ .separator__hr {
+ border-color: #ccc;
+ margin: 0;
+ position: relative;
+ z-index: 5;
+ top: 1em;
}
+ .separator__text {
+ line-height: 2em;
+ color: #555;
+ background: #FFF;
+ display: inline-block;
+ padding: 0 1em;
+ font-weight: 700;
+ @include border-radius(50px);
+ position: relative;
+ z-index: 5;
+ font-size: 13px;
+ }
+}
+.new-separator {
+ .separator__hr {
+ border-color: #FFAF53;
+ }
+ .separator__text {
+ color: #F80;
+ font-weight: normal;
+ }
+}
+
+#post-list {
.post-list-holder-by-time {
background: #fff;
overflow-y: scroll;
@@ -135,6 +136,7 @@ body.ios {
color: grey;
}
}
+
.post-create__container {
form {
width: 100%;
@@ -306,7 +308,7 @@ body.ios {
}
.post-image__columns {
@include legacy-pie-clearfix;
- margin-top: 1em;
+ padding-bottom: 5px;
}
.post-info--hidden {
display: none;
@@ -347,7 +349,7 @@ body.ios {
}
&.post-info {
.post-profile-time {
- width: 100px;
+ width: 150px;
display: inline-block;
margin-left: 50px;
}
diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss
index 1a2befc3f..0eeaa3615 100644
--- a/web/sass-files/sass/partials/_responsive.scss
+++ b/web/sass-files/sass/partials/_responsive.scss
@@ -1,6 +1,13 @@
@media screen and (max-width: 1800px) {
.inner__wrap {
&.move--left {
+ .date-separator, .new-separator {
+ &.hovered--comment {
+ &:before, &:after {
+ background: none;
+ }
+ }
+ }
.post {
&.same--root {
margin-left: 60px;
@@ -82,6 +89,13 @@
max-width: 810px;
}
}
+ .date-separator, .new-separator {
+ &.hovered--comment {
+ &:before, &:after {
+ background: none;
+ }
+ }
+ }
.post {
&.same--root {
margin-left: 60px;
@@ -207,17 +221,15 @@
}
@media screen and (max-width: 768px) {
- #post-list {
- .date-seperator, .new-separator {
- &.hovered--after {
- &:before {
- background: none;
- }
+ .date-separator, .new-separator {
+ &.hovered--after {
+ &:before {
+ background: none;
}
- &.hovered--before {
- &:after {
- background: none;
- }
+ }
+ &.hovered--before {
+ &:after {
+ background: none;
}
}
}