Merge pull request #383 from rgarmsen2295/mm-1584b

MM-1584 Private message channels no longer do a page refresh on creation/first use
Этот коммит содержится в:
Corey Hulen
2015-08-18 21:19:15 -07:00
родитель 44464f8f3c f2b06cfc7c
Коммит 5bf1aac697
5 изменённых файлов: 152 добавлений и 52 удалений

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

@@ -3,67 +3,102 @@
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var TeamStore = require('../stores/team_store.jsx'); var TeamStore = require('../stores/team_store.jsx');
var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'MoreDirectChannels',
componentDidMount: function() { componentDidMount: function() {
var self = this; var self = this;
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { $(this.refs.modal.getDOMNode()).on('show.bs.modal', function showModal(e) {
var button = e.relatedTarget; var button = e.relatedTarget;
self.setState({ channels: $(button).data('channels') }); self.setState({channels: $(button).data('channels')});
}); });
}, },
getInitialState: function() { getInitialState: function() {
return { channels: [] }; return {channels: [], loadingDMChannel: -1};
}, },
render: function() { render: function() {
var self = this; var self = this;
var directMessageItems = this.state.channels.map(function(channel) { var directMessageItems = this.state.channels.map(function mapActivityToChannel(channel, index) {
var badge = ""; var badge = '';
var titleClass = "" var titleClass = '';
var active = '';
var handleClick = null;
if (!channel.fake) { if (!channel.fake) {
var active = channel.id === ChannelStore.getCurrentId() ? "active" : ""; if (channel.id === ChannelStore.getCurrentId()) {
active = 'active';
}
if (channel.unread) { 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"
} }
return (
<li key={channel.name} className={active}><a className={"sidebar-channel " + titleClass} href="#" onClick={function(e){e.preventDefault(); utils.switchChannel(channel, channel.teammate_username); $(self.refs.modal.getDOMNode()).modal('hide')}}>{badge}{channel.display_name}</a></li> handleClick = function clickHandler(e) {
); e.preventDefault();
utils.switchChannel(channel, channel.teammate_username);
$(self.refs.modal.getDOMNode()).modal('hide');
};
} else { } else {
return ( // It's a direct message channel that doesn't exist yet so let's create it now
<li key={channel.name} className={active}><a className={"sidebar-channel " + titleClass} href={TeamStore.getCurrentTeamUrl() + "/channels/"+channel.name}>{badge}{channel.display_name}</a></li> var otherUserId = utils.getUserIdFromChannelName(channel);
);
if (self.state.loadingDMChannel === index) {
badge = <img className='channel-loading-gif pull-right' src='/static/images/load.gif'/>;
}
if (self.state.loadingDMChannel === -1) {
handleClick = function clickHandler(e) {
e.preventDefault();
self.setState({loadingDMChannel: index});
Client.createDirectChannel(channel, otherUserId,
function success(data) {
$(self.refs.modal.getDOMNode()).modal('hide');
self.setState({loadingDMChannel: -1});
AsyncClient.getChannel(data.id);
utils.switchChannel(data);
},
function error() {
self.setState({loadingDMChannel: -1});
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name;
}
);
};
}
} }
return (
<li key={channel.name} className={active}><a className={'sidebar-channel ' + titleClass} href='#' onClick={handleClick}>{badge}{channel.display_name}</a></li>
);
}); });
return ( return (
<div className="modal fade" id="more_direct_channels" ref="modal" tabIndex="-1" role="dialog" aria-hidden="true"> <div className='modal fade' id='more_direct_channels' ref='modal' tabIndex='-1' role='dialog' aria-hidden='true'>
<div className="modal-dialog"> <div className='modal-dialog'>
<div className="modal-content"> <div className='modal-content'>
<div className="modal-header"> <div className='modal-header'>
<button type="button" className="close" data-dismiss="modal"> <button type='button' className='close' data-dismiss='modal'>
<span aria-hidden="true">&times;</span> <span aria-hidden='true'>&times;</span>
<span className="sr-only">Close</span> <span className='sr-only'>Close</span>
</button> </button>
<h4 className="modal-title">More Private Messages</h4> <h4 className='modal-title'>More Private Messages</h4>
</div> </div>
<div className="modal-body"> <div className='modal-body'>
<ul className="nav nav-pills nav-stacked"> <ul className='nav nav-pills nav-stacked'>
{directMessageItems} {directMessageItems}
</ul> </ul>
</div> </div>
<div className="modal-footer"> <div className='modal-footer'>
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button> <button type='button' className='btn btn-default' data-dismiss='modal'>Close</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); );
} }
}); });

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

@@ -1,8 +1,8 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
var SocketStore = require('../stores/socket_store.jsx'); var SocketStore = require('../stores/socket_store.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
@@ -11,9 +11,7 @@ var BrowserStore = require('../stores/browser_store.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
var SidebarHeader = require('./sidebar_header.jsx'); var SidebarHeader = require('./sidebar_header.jsx');
var SearchBox = require('./search_bar.jsx'); var SearchBox = require('./search_bar.jsx');
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
function getStateFromStores() { function getStateFromStores() {
var members = ChannelStore.getAllMembers(); var members = ChannelStore.getAllMembers();
@@ -70,13 +68,14 @@ function getStateFromStores() {
tempChannel.status = UserStore.getStatus(teammate.id); tempChannel.status = UserStore.getStatus(teammate.id);
tempChannel.last_post_at = 0; tempChannel.last_post_at = 0;
tempChannel.total_msg_count = 0; tempChannel.total_msg_count = 0;
tempChannel.type = 'D';
readDirectChannels.push(tempChannel); readDirectChannels.push(tempChannel);
} }
} }
// If we don't have MAX_DMS unread channels, sort the read list by last_post_at // If we don't have MAX_DMS unread channels, sort the read list by last_post_at
if (showDirectChannels.length < Constants.MAX_DMS) { if (showDirectChannels.length < Constants.MAX_DMS) {
readDirectChannels.sort(function(a, b) { readDirectChannels.sort(function sortByLastPost(a, b) {
// sort by last_post_at first // sort by last_post_at first
if (a.last_post_at > b.last_post_at) { if (a.last_post_at > b.last_post_at) {
return -1; return -1;
@@ -124,6 +123,10 @@ function getStateFromStores() {
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'Sidebar', displayName: 'Sidebar',
propTypes: {
teamType: React.PropTypes.string,
teamDisplayName: React.PropTypes.string
},
componentDidMount: function() { componentDidMount: function() {
ChannelStore.addChangeListener(this.onChange); ChannelStore.addChangeListener(this.onChange);
UserStore.addChangeListener(this.onChange); UserStore.addChangeListener(this.onChange);
@@ -244,17 +247,17 @@ module.exports = React.createClass({
var channel = ChannelStore.getCurrent(); var channel = ChannelStore.getCurrent();
if (channel) { if (channel) {
if (channel.type === 'D') { if (channel.type === 'D') {
var teammate_username = utils.getDirectTeammate(channel.id).username; var teammateUsername = utils.getDirectTeammate(channel.id).username;
document.title = teammate_username + ' ' + document.title.substring(document.title.lastIndexOf('-')); document.title = teammateUsername + ' ' + document.title.substring(document.title.lastIndexOf('-'));
} else { } else {
document.title = channel.display_name + ' ' + document.title.substring(document.title.lastIndexOf('-')); document.title = channel.display_name + ' ' + document.title.substring(document.title.lastIndexOf('-'));
} }
} }
}, },
onScroll: function(e) { onScroll: function() {
this.updateUnreadIndicators(); this.updateUnreadIndicators();
}, },
onResize: function(e) { onResize: function() {
this.updateUnreadIndicators(); this.updateUnreadIndicators();
}, },
updateUnreadIndicators: function() { updateUnreadIndicators: function() {
@@ -282,7 +285,10 @@ module.exports = React.createClass({
} }
}, },
getInitialState: function() { getInitialState: function() {
return getStateFromStores(); var newState = getStateFromStores();
newState.loadingDMChannel = -1;
return newState;
}, },
render: function() { render: function() {
var members = this.state.members; var members = this.state.members;
@@ -294,8 +300,9 @@ module.exports = React.createClass({
this.firstUnreadChannel = null; this.firstUnreadChannel = null;
this.lastUnreadChannel = null; this.lastUnreadChannel = null;
function createChannelElement(channel) { function createChannelElement(channel, index) {
var channelMember = members[channel.id]; var channelMember = members[channel.id];
var msgCount;
var linkClass = ''; var linkClass = '';
if (channel.id === activeId) { if (channel.id === activeId) {
@@ -304,7 +311,7 @@ module.exports = React.createClass({
var unread = false; var unread = false;
if (channelMember) { if (channelMember) {
var msgCount = channel.total_msg_count - channelMember.msg_count; msgCount = channel.total_msg_count - channelMember.msg_count;
unread = (msgCount > 0 && channelMember.notify_level !== 'quiet') || channelMember.mention_count > 0; unread = (msgCount > 0 && channelMember.notify_level !== 'quiet') || channelMember.mention_count > 0;
} }
@@ -322,7 +329,7 @@ module.exports = React.createClass({
if (channelMember) { if (channelMember) {
if (channel.type === 'D') { if (channel.type === 'D') {
// direct message channels show badges for any number of unread posts // direct message channels show badges for any number of unread posts
var msgCount = channel.total_msg_count - channelMember.msg_count; msgCount = channel.total_msg_count - channelMember.msg_count;
if (msgCount > 0) { if (msgCount > 0) {
badge = <span className='badge pull-right small'>{msgCount}</span>; badge = <span className='badge pull-right small'>{msgCount}</span>;
badgesActive = true; badgesActive = true;
@@ -332,6 +339,8 @@ module.exports = React.createClass({
badge = <span className='badge pull-right small'>{channelMember.mention_count}</span>; badge = <span className='badge pull-right small'>{channelMember.mention_count}</span>;
badgesActive = true; badgesActive = true;
} }
} else if (self.state.loadingDMChannel === index && channel.type === 'D') {
badge = <img className='channel-loading-gif pull-right' src='/static/images/load.gif'/>;
} }
// set up status icon for direct message channels // set up status icon for direct message channels
@@ -349,39 +358,59 @@ module.exports = React.createClass({
} }
// set up click handler to switch channels (or create a new channel for non-existant ones) // set up click handler to switch channels (or create a new channel for non-existant ones)
var clickHandler = null; var handleClick = null;
var href = '#'; var href = '#';
var teamURL = TeamStore.getCurrentTeamUrl(); var teamURL = TeamStore.getCurrentTeamUrl();
if (!channel.fake) { if (!channel.fake) {
clickHandler = function(e) { handleClick = function clickHandler(e) {
e.preventDefault(); e.preventDefault();
utils.switchChannel(channel); utils.switchChannel(channel);
}; };
} } else if (channel.fake && teamURL) {
if (channel.fake && teamURL){ // It's a direct message channel that doesn't exist yet so let's create it now
href = teamURL + '/channels/' + channel.name; var otherUserId = utils.getUserIdFromChannelName(channel);
if (self.state.loadingDMChannel === -1) {
handleClick = function clickHandler(e) {
e.preventDefault();
self.setState({loadingDMChannel: index});
Client.createDirectChannel(channel, otherUserId,
function success(data) {
self.setState({loadingDMChannel: -1});
AsyncClient.getChannel(data.id);
utils.switchChannel(data);
},
function error() {
self.setState({loadingDMChannel: -1});
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name;
}
);
};
}
} }
return ( return (
<li key={channel.name} ref={channel.name} className={linkClass}> <li key={channel.name} ref={channel.name} className={linkClass}>
<a className={'sidebar-channel ' + titleClass} href={href} onClick={clickHandler}> <a className={'sidebar-channel ' + titleClass} href={href} onClick={handleClick}>
{status} {status}
{channel.display_name} {channel.display_name}
{badge} {badge}
</a> </a>
</li> </li>
); );
}; }
// create elements for all 3 types of channels // create elements for all 3 types of channels
var channelItems = this.state.channels.filter( var channelItems = this.state.channels.filter(
function(channel) { function filterPublicChannels(channel) {
return channel.type === 'O'; return channel.type === 'O';
} }
).map(createChannelElement); ).map(createChannelElement);
var privateChannelItems = this.state.channels.filter( var privateChannelItems = this.state.channels.filter(
function(channel) { function filterPrivateChannels(channel) {
return channel.type === 'P'; return channel.type === 'P';
} }
).map(createChannelElement); ).map(createChannelElement);
@@ -410,7 +439,7 @@ module.exports = React.createClass({
directMessageMore = ( directMessageMore = (
<li> <li>
<a href='#' data-toggle='modal' className='nav-more' data-target='#more_direct_channels' data-channels={JSON.stringify(this.state.hideDirectChannels)}> <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+')'} {'More (' + this.state.hideDirectChannels.length + ')'}
</a> </a>
</li> </li>
); );

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

@@ -438,6 +438,23 @@ module.exports.createChannel = function(channel, success, error) {
module.exports.track('api', 'api_channels_create', channel.type, 'name', channel.name); module.exports.track('api', 'api_channels_create', channel.type, 'name', channel.name);
}; };
module.exports.createDirectChannel = function(channel, userId, success, error) {
$.ajax({
url: '/api/v1/channels/create_direct',
dataType: 'json',
contentType: 'application/json',
type: 'POST',
data: JSON.stringify({user_id: userId}),
success: success,
error: function(xhr, status, err) {
var e = handleError('createDirectChannel', xhr, status, err);
error(e);
}
});
module.exports.track('api', 'api_channels_create_direct', channel.type, 'name', channel.name);
};
module.exports.updateChannel = function(channel, success, error) { module.exports.updateChannel = function(channel, success, error) {
$.ajax({ $.ajax({
url: "/api/v1/channels/update", url: "/api/v1/channels/update",

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

@@ -968,4 +968,17 @@ module.exports.generateId = function() {
module.exports.isBrowserFirefox = function() { module.exports.isBrowserFirefox = function() {
return navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox') > -1; return navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
} };
// Used to get the id of the other user from a DM channel
module.exports.getUserIdFromChannelName = function(channel) {
var ids = channel.name.split('__');
var otherUserId = '';
if (ids[0] === UserStore.getCurrentId()) {
otherUserId = ids[1];
} else {
otherUserId = ids[0];
}
return otherUserId;
};

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

@@ -122,3 +122,9 @@
} }
} }
} }
.channel-loading-gif {
height:15px;
width:15px;
margin-top:2px;
}