merging
Этот коммит содержится в:
@@ -64,9 +64,14 @@ export default class ChannelHeader extends React.Component {
|
||||
handleLeave() {
|
||||
Client.leaveChannel(this.state.channel.id,
|
||||
function handleLeaveSuccess() {
|
||||
AppDispatcher.handleViewAction({
|
||||
type: ActionTypes.LEAVE_CHANNEL,
|
||||
id: this.state.channel.id
|
||||
});
|
||||
|
||||
const townsquare = ChannelStore.getByName('town-square');
|
||||
Utils.switchChannel(townsquare);
|
||||
},
|
||||
}.bind(this),
|
||||
function handleLeaveError(err) {
|
||||
AsyncClient.dispatchError(err, 'handleLeave');
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ export default class ChannelLoader extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.intervalId = null;
|
||||
|
||||
this.onSocketChange = this.onSocketChange.bind(this);
|
||||
|
||||
this.state = {};
|
||||
@@ -35,10 +37,12 @@ export default class ChannelLoader extends React.Component {
|
||||
PostStore.clearPendingPosts();
|
||||
|
||||
/* Set up interval functions */
|
||||
setInterval(
|
||||
this.intervalId = setInterval(
|
||||
function pollStatuses() {
|
||||
AsyncClient.getStatuses();
|
||||
}, 30000);
|
||||
},
|
||||
30000
|
||||
);
|
||||
|
||||
/* Device tracking setup */
|
||||
var iOS = (/(iPad|iPhone|iPod)/g).test(navigator.userAgent);
|
||||
@@ -49,12 +53,12 @@ export default class ChannelLoader extends React.Component {
|
||||
/* Set up tracking for whether the window is active */
|
||||
window.isActive = true;
|
||||
|
||||
$(window).focus(function windowFocus() {
|
||||
$(window).on('focus', function windowFocus() {
|
||||
AsyncClient.updateLastViewedAt();
|
||||
window.isActive = true;
|
||||
});
|
||||
|
||||
$(window).blur(function windowBlur() {
|
||||
$(window).on('blur', function windowBlur() {
|
||||
window.isActive = false;
|
||||
});
|
||||
|
||||
@@ -84,6 +88,54 @@ export default class ChannelLoader extends React.Component {
|
||||
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');
|
||||
}
|
||||
|
||||
/* Setup global mouse events */
|
||||
$('body').on('click.userpopover', function popOver(e) {
|
||||
if ($(e.target).attr('data-toggle') !== 'popover' &&
|
||||
$(e.target).parents('.popover.in').length === 0) {
|
||||
$('.user-popover').popover('hide');
|
||||
}
|
||||
});
|
||||
|
||||
$('body').on('mouseenter mouseleave', '.post', function mouseOver(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');
|
||||
} else {
|
||||
$(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--after');
|
||||
$(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--before');
|
||||
}
|
||||
});
|
||||
|
||||
$('body').on('mouseenter mouseleave', '.post.post--comment.same--root', function mouseOver(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');
|
||||
}
|
||||
});
|
||||
|
||||
/* Setup modal events */
|
||||
$('.modal').on('show.bs.modal', function onShow() {
|
||||
$('.modal-body').css('overflow-y', 'auto');
|
||||
$('.modal-body').css('max-height', $(window).height() * 0.7);
|
||||
});
|
||||
}
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.intervalId);
|
||||
|
||||
$(window).off('focus');
|
||||
$(window).off('blur');
|
||||
|
||||
SocketStore.removeChangeListener(this.onSocketChange);
|
||||
|
||||
$('body').off('click.userpopover');
|
||||
$('body').off('mouseenter mouseleave', '.post');
|
||||
$('body').off('mouseenter mouseleave', '.post.post--comment.same--root');
|
||||
|
||||
$('.modal').off('show.bs.modal');
|
||||
}
|
||||
onSocketChange(msg) {
|
||||
if (msg && msg.user_id && msg.user_id !== UserStore.getCurrentId()) {
|
||||
|
||||
@@ -55,6 +55,11 @@ export default class CreatePost extends React.Component {
|
||||
initialText: messageText
|
||||
};
|
||||
}
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState.previews.length !== this.state.previews.length) {
|
||||
this.resizePostHolder();
|
||||
}
|
||||
}
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -310,25 +315,33 @@ export default class CreatePost extends React.Component {
|
||||
>
|
||||
<div className='post-create'>
|
||||
<div className='post-create-body'>
|
||||
<Textbox
|
||||
onUserInput={this.handleUserInput}
|
||||
onKeyPress={this.postMsgKeyPress}
|
||||
onHeightChange={this.resizePostHolder}
|
||||
messageText={this.state.messageText}
|
||||
createMessage='Write a message...'
|
||||
channelId={this.state.channelId}
|
||||
id='post_textbox'
|
||||
ref='textbox'
|
||||
/>
|
||||
<FileUpload
|
||||
ref='fileUpload'
|
||||
getFileCount={this.getFileCount}
|
||||
onUploadStart={this.handleUploadStart}
|
||||
onFileUpload={this.handleFileUploadComplete}
|
||||
onUploadError={this.handleUploadError}
|
||||
postType='post'
|
||||
channelId=''
|
||||
/>
|
||||
<div className='post-body__cell'>
|
||||
<Textbox
|
||||
onUserInput={this.handleUserInput}
|
||||
onKeyPress={this.postMsgKeyPress}
|
||||
onHeightChange={this.resizePostHolder}
|
||||
messageText={this.state.messageText}
|
||||
createMessage='Write a message...'
|
||||
channelId={this.state.channelId}
|
||||
id='post_textbox'
|
||||
ref='textbox'
|
||||
/>
|
||||
<FileUpload
|
||||
ref='fileUpload'
|
||||
getFileCount={this.getFileCount}
|
||||
onUploadStart={this.handleUploadStart}
|
||||
onFileUpload={this.handleFileUploadComplete}
|
||||
onUploadError={this.handleUploadError}
|
||||
postType='post'
|
||||
channelId=''
|
||||
/>
|
||||
</div>
|
||||
<a
|
||||
className='send-button theme'
|
||||
onClick={this.handleSubmit}
|
||||
>
|
||||
<i className='fa fa-paper-plane' />
|
||||
</a>
|
||||
</div>
|
||||
<div className={postFooterClassName}>
|
||||
{postError}
|
||||
|
||||
@@ -97,6 +97,7 @@ export default class FileAttachment extends React.Component {
|
||||
var filename = this.props.filename;
|
||||
|
||||
var fileInfo = utils.splitFileLocation(filename);
|
||||
var fileUrl = utils.getFileUrl(filename);
|
||||
var type = utils.getFileType(fileInfo.ext);
|
||||
|
||||
var thumbnail;
|
||||
@@ -150,14 +151,25 @@ export default class FileAttachment extends React.Component {
|
||||
{thumbnail}
|
||||
</a>
|
||||
<div className='post-image__details'>
|
||||
<div
|
||||
<a
|
||||
href={fileUrl}
|
||||
download={filenameString}
|
||||
data-toggle='tooltip'
|
||||
title={filenameString}
|
||||
className='post-image__name'
|
||||
>
|
||||
{trimmedFilename}
|
||||
</div>
|
||||
</a>
|
||||
<div>
|
||||
<a
|
||||
href={fileUrl}
|
||||
download={filenameString}
|
||||
className='post-image__download'
|
||||
>
|
||||
<span
|
||||
className='fa fa-download'
|
||||
/>
|
||||
</a>
|
||||
<span className='post-image__type'>{fileInfo.ext.toUpperCase()}</span>
|
||||
<span className='post-image__size'>{fileSizeString}</span>
|
||||
</div>
|
||||
|
||||
@@ -18,8 +18,8 @@ var ActionTypes = Constants.ActionTypes;
|
||||
import {strings} from '../utils/config.js';
|
||||
|
||||
export default class PostList extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.gotMorePosts = false;
|
||||
this.scrolled = false;
|
||||
@@ -27,6 +27,7 @@ export default class PostList extends React.Component {
|
||||
this.seenNewMessages = false;
|
||||
this.isUserScroll = true;
|
||||
this.userHasSeenNew = false;
|
||||
this.loadInProgress = false;
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onTimeChange = this.onTimeChange.bind(this);
|
||||
@@ -34,22 +35,19 @@ export default class PostList extends React.Component {
|
||||
this.createChannelIntroMessage = this.createChannelIntroMessage.bind(this);
|
||||
this.loadMorePosts = this.loadMorePosts.bind(this);
|
||||
this.loadFirstPosts = this.loadFirstPosts.bind(this);
|
||||
this.activate = this.activate.bind(this);
|
||||
this.deactivate = this.deactivate.bind(this);
|
||||
this.resize = this.resize.bind(this);
|
||||
|
||||
this.state = this.getStateFromStores();
|
||||
this.state = this.getStateFromStores(props.channelId);
|
||||
this.state.numToDisplay = Constants.POST_CHUNK_SIZE;
|
||||
this.state.isFirstLoadComplete = false;
|
||||
}
|
||||
getStateFromStores() {
|
||||
var channel = ChannelStore.getCurrent();
|
||||
|
||||
if (channel == null) {
|
||||
channel = {};
|
||||
}
|
||||
|
||||
var postList = PostStore.getCurrentPosts();
|
||||
getStateFromStores(id) {
|
||||
var postList = PostStore.getPosts(id);
|
||||
|
||||
if (postList != null) {
|
||||
var deletedPosts = PostStore.getUnseenDeletedPosts(channel.id);
|
||||
var deletedPosts = PostStore.getUnseenDeletedPosts(id);
|
||||
|
||||
if (deletedPosts && Object.keys(deletedPosts).length > 0) {
|
||||
for (var pid in deletedPosts) {
|
||||
@@ -70,7 +68,7 @@ export default class PostList extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
var pendingPostList = PostStore.getPendingPosts(channel.id);
|
||||
var pendingPostList = PostStore.getPendingPosts(id);
|
||||
|
||||
if (pendingPostList) {
|
||||
postList.order = pendingPostList.order.concat(postList.order);
|
||||
@@ -82,43 +80,42 @@ export default class PostList extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
var lastViewed = Number.MAX_VALUE;
|
||||
|
||||
if (ChannelStore.getCurrentMember() != null) {
|
||||
lastViewed = ChannelStore.getCurrentMember().last_viewed_at;
|
||||
}
|
||||
|
||||
return {
|
||||
postList: postList,
|
||||
channel: channel,
|
||||
lastViewed: lastViewed
|
||||
postList: postList
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
if (this.props.isActive) {
|
||||
this.activate();
|
||||
this.loadFirstPosts(this.props.channelId);
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.deactivate();
|
||||
}
|
||||
activate() {
|
||||
this.gotMorePosts = false;
|
||||
this.scrolled = false;
|
||||
this.prevScrollTop = 0;
|
||||
this.seenNewMessages = false;
|
||||
this.isUserScroll = true;
|
||||
this.userHasSeenNew = false;
|
||||
|
||||
PostStore.clearUnseenDeletedPosts(this.props.channelId);
|
||||
PostStore.addChangeListener(this.onChange);
|
||||
ChannelStore.addChangeListener(this.onChange);
|
||||
UserStore.addStatusesChangeListener(this.onTimeChange);
|
||||
SocketStore.addChangeListener(this.onSocketChange);
|
||||
|
||||
var postHolder = $('.post-list-holder-by-time');
|
||||
|
||||
$('.modal').on('show.bs.modal', function onShow() {
|
||||
$('.modal-body').css('overflow-y', 'auto');
|
||||
$('.modal-body').css('max-height', $(window).height() * 0.7);
|
||||
});
|
||||
|
||||
$(window).resize(function resize() {
|
||||
if ($('#create_post').length > 0) {
|
||||
var height = $(window).height() - $('#create_post').height() - $('#error_bar').outerHeight() - 50;
|
||||
postHolder.css('height', height + 'px');
|
||||
}
|
||||
var postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
|
||||
$(window).on('resize.' + this.props.channelId, function resize() {
|
||||
this.resize();
|
||||
if (!this.scrolled) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
postHolder.scroll(function scroll() {
|
||||
postHolder.on('scroll', function scroll() {
|
||||
var position = postHolder.scrollTop() + postHolder.height() + 14;
|
||||
var bottom = postHolder[0].scrollHeight;
|
||||
|
||||
@@ -134,43 +131,31 @@ export default class PostList extends React.Component {
|
||||
this.isUserScroll = true;
|
||||
}.bind(this));
|
||||
|
||||
$('body').on('click.userpopover', function popOver(e) {
|
||||
if ($(e.target).attr('data-toggle') !== 'popover' &&
|
||||
$(e.target).parents('.popover.in').length === 0) {
|
||||
$('.user-popover').popover('hide');
|
||||
}
|
||||
});
|
||||
|
||||
$('.post-list__content div .post').removeClass('post--last');
|
||||
$('.post-list__content div:last-child .post').addClass('post--last');
|
||||
|
||||
$('body').on('mouseenter mouseleave', '.post', function mouseOver(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');
|
||||
} else {
|
||||
$(this).parent('div').prev('.date-separator, .new-separator').removeClass('hovered--after');
|
||||
$(this).parent('div').next('.date-separator, .new-separator').removeClass('hovered--before');
|
||||
}
|
||||
});
|
||||
|
||||
$('body').on('mouseenter mouseleave', '.post.post--comment.same--root', function mouseOver(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');
|
||||
}
|
||||
});
|
||||
|
||||
this.scrollToBottom();
|
||||
|
||||
if (this.state.channel.id != null) {
|
||||
this.loadFirstPosts(this.state.channel.id);
|
||||
if (!this.state.isFirstLoadComplete) {
|
||||
this.loadFirstPosts(this.props.channelId);
|
||||
}
|
||||
|
||||
this.resize();
|
||||
this.onChange();
|
||||
this.scrollToBottom();
|
||||
}
|
||||
deactivate() {
|
||||
PostStore.removeChangeListener(this.onChange);
|
||||
UserStore.removeStatusesChangeListener(this.onTimeChange);
|
||||
SocketStore.removeChangeListener(this.onSocketChange);
|
||||
$('body').off('click.userpopover');
|
||||
$(window).off('resize.' + this.props.channelId);
|
||||
var postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
postHolder.off('scroll');
|
||||
}
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (!this.props.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('.post-list__content div .post').removeClass('post--last');
|
||||
$('.post-list__content div:last-child .post').addClass('post--last');
|
||||
|
||||
@@ -187,7 +172,7 @@ export default class PostList extends React.Component {
|
||||
var firstPost = posts[order[0]] || {};
|
||||
var isNewPost = oldOrder.indexOf(order[0]) === -1;
|
||||
|
||||
if (this.state.channel.id !== prevState.channel.id) {
|
||||
if (this.props.isActive && !prevProps.isActive) {
|
||||
this.scrollToBottom();
|
||||
} else if (oldOrder.length === 0) {
|
||||
this.scrollToBottom();
|
||||
@@ -201,39 +186,45 @@ export default class PostList extends React.Component {
|
||||
} else if (isNewPost &&
|
||||
userId === firstPost.user_id &&
|
||||
!utils.isComment(firstPost)) {
|
||||
this.state.lastViewed = utils.getTimestamp();
|
||||
this.scrollToBottom(true);
|
||||
|
||||
// the user clicked 'load more messages'
|
||||
} else if (this.gotMorePosts) {
|
||||
var lastPost = oldPosts[oldOrder[prevState.numToDisplay]];
|
||||
$('#' + lastPost.id)[0].scrollIntoView();
|
||||
this.gotMorePosts = false;
|
||||
} else {
|
||||
this.scrollTo(this.prevScrollTop);
|
||||
}
|
||||
}
|
||||
componentWillUpdate() {
|
||||
var postHolder = $('.post-list-holder-by-time');
|
||||
var postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
this.prevScrollTop = postHolder.scrollTop();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
PostStore.removeChangeListener(this.onChange);
|
||||
ChannelStore.removeChangeListener(this.onChange);
|
||||
UserStore.removeStatusesChangeListener(this.onTimeChange);
|
||||
SocketStore.removeChangeListener(this.onSocketChange);
|
||||
$('body').off('click.userpopover');
|
||||
$('.modal').off('show.bs.modal');
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.isActive === true && this.props.isActive === false) {
|
||||
this.activate();
|
||||
} else if (nextProps.isActive === false && this.props.isActive === true) {
|
||||
this.deactivate();
|
||||
}
|
||||
}
|
||||
resize() {
|
||||
const postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
if ($('#create_post').length > 0) {
|
||||
const height = $(window).height() - $('#create_post').height() - $('#error_bar').outerHeight() - 50;
|
||||
postHolder.css('height', height + 'px');
|
||||
}
|
||||
}
|
||||
scrollTo(val) {
|
||||
this.isUserScroll = false;
|
||||
var postHolder = $('.post-list-holder-by-time');
|
||||
var postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
postHolder[0].scrollTop = val;
|
||||
}
|
||||
scrollToBottom(force) {
|
||||
this.isUserScroll = false;
|
||||
var postHolder = $('.post-list-holder-by-time');
|
||||
if ($('#new_message')[0] && !this.userHasSeenNew && !force) {
|
||||
$('#new_message')[0].scrollIntoView();
|
||||
var postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
if ($('#new_message_' + this.props.channelId)[0] && !this.userHasSeenNew && !force) {
|
||||
$('#new_message_' + this.props.channelId)[0].scrollIntoView();
|
||||
} else {
|
||||
postHolder.addClass('hide-scroll');
|
||||
postHolder[0].scrollTop = postHolder[0].scrollHeight;
|
||||
@@ -241,34 +232,32 @@ export default class PostList extends React.Component {
|
||||
}
|
||||
}
|
||||
loadFirstPosts(id) {
|
||||
if (this.loadInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.channelId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadInProgress = true;
|
||||
Client.getPosts(
|
||||
id,
|
||||
PostStore.getLatestUpdate(id),
|
||||
function success() {
|
||||
this.loadInProgress = false;
|
||||
this.setState({isFirstLoadComplete: true});
|
||||
}.bind(this),
|
||||
function fail() {
|
||||
this.loadInProgress = false;
|
||||
this.setState({isFirstLoadComplete: true});
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
onChange() {
|
||||
var newState = this.getStateFromStores();
|
||||
|
||||
// Special case where the channel wasn't yet set in componentDidMount
|
||||
if (!this.state.isFirstLoadComplete && this.state.channel.id == null && newState.channel.id != null) {
|
||||
this.loadFirstPosts(newState.channel.id);
|
||||
}
|
||||
|
||||
if (!utils.areStatesEqual(newState, this.state)) {
|
||||
if (this.state.channel.id !== newState.channel.id) {
|
||||
PostStore.clearUnseenDeletedPosts(this.state.channel.id);
|
||||
this.userHasSeenNew = false;
|
||||
newState.numToDisplay = Constants.POST_CHUNK_SIZE;
|
||||
} else {
|
||||
newState.lastViewed = this.state.lastViewed;
|
||||
}
|
||||
var newState = this.getStateFromStores(this.props.channelId);
|
||||
|
||||
if (!utils.areStatesEqual(newState.postList, this.state.postList)) {
|
||||
this.setState(newState);
|
||||
}
|
||||
}
|
||||
@@ -424,7 +413,7 @@ export default class PostList extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
var members = ChannelStore.getCurrentExtraInfo().members;
|
||||
var members = ChannelStore.getExtraInfo(channel.id).members;
|
||||
for (var i = 0; i < members.length; i++) {
|
||||
if (members[i].roles.indexOf('admin') > -1) {
|
||||
return members[i].username;
|
||||
@@ -488,6 +477,11 @@ export default class PostList extends React.Component {
|
||||
var userId = UserStore.getCurrentId();
|
||||
|
||||
var renderedLastViewed = false;
|
||||
var lastViewed = Number.MAX_VALUE;
|
||||
|
||||
if (ChannelStore.getMember(this.props.channelId) != null) {
|
||||
lastViewed = ChannelStore.getMember(this.props.channelId).last_viewed_at;
|
||||
}
|
||||
|
||||
var numToDisplay = this.state.numToDisplay;
|
||||
if (order.length - 1 < numToDisplay) {
|
||||
@@ -543,13 +537,13 @@ export default class PostList extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (post.user_id !== userId && post.create_at > this.state.lastViewed && !renderedLastViewed) {
|
||||
if (post.user_id !== userId && post.create_at > lastViewed && !renderedLastViewed) {
|
||||
renderedLastViewed = true;
|
||||
|
||||
// Temporary fix to solve ie10/11 rendering issue
|
||||
let newSeparatorId = '';
|
||||
if (!utils.isBrowserIE()) {
|
||||
newSeparatorId = 'new_message';
|
||||
newSeparatorId = 'new_message_' + this.props.channelId;
|
||||
}
|
||||
postCtls.push(
|
||||
<div
|
||||
@@ -577,7 +571,7 @@ export default class PostList extends React.Component {
|
||||
|
||||
var posts = this.state.postList.posts;
|
||||
var order = this.state.postList.order;
|
||||
var channelId = this.state.channel.id;
|
||||
var channelId = this.props.channelId;
|
||||
|
||||
$(React.findDOMNode(this.refs.loadmore)).text('Retrieving more messages...');
|
||||
|
||||
@@ -619,7 +613,7 @@ export default class PostList extends React.Component {
|
||||
render() {
|
||||
var order = [];
|
||||
var posts;
|
||||
var channel = this.state.channel;
|
||||
var channel = ChannelStore.get(this.props.channelId);
|
||||
|
||||
if (this.state.postList != null) {
|
||||
posts = this.state.postList.posts;
|
||||
@@ -628,7 +622,7 @@ export default class PostList extends React.Component {
|
||||
|
||||
var moreMessages = <p className='beginning-messages-text'>Beginning of Channel</p>;
|
||||
if (channel != null) {
|
||||
if (order.length > this.state.numToDisplay) {
|
||||
if (order.length >= this.state.numToDisplay) {
|
||||
moreMessages = (
|
||||
<a
|
||||
ref='loadmore'
|
||||
@@ -655,10 +649,15 @@ export default class PostList extends React.Component {
|
||||
/>);
|
||||
}
|
||||
|
||||
var activeClass = '';
|
||||
if (!this.props.isActive) {
|
||||
activeClass = 'inactive';
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref='postlist'
|
||||
className='post-list-holder-by-time'
|
||||
className={'post-list-holder-by-time ' + activeClass}
|
||||
>
|
||||
<div className='post-list__table'>
|
||||
<div className='post-list__content'>
|
||||
@@ -670,3 +669,12 @@ export default class PostList extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PostList.defaultProps = {
|
||||
isActive: false,
|
||||
channelId: null
|
||||
};
|
||||
PostList.propTypes = {
|
||||
isActive: React.PropTypes.bool,
|
||||
channelId: React.PropTypes.string
|
||||
};
|
||||
|
||||
62
web/react/components/post_list_container.jsx
Обычный файл
62
web/react/components/post_list_container.jsx
Обычный файл
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const PostList = require('./post_list.jsx');
|
||||
const ChannelStore = require('../stores/channel_store.jsx');
|
||||
|
||||
export default class PostListContainer extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onLeave = this.onLeave.bind(this);
|
||||
|
||||
let currentChannelId = ChannelStore.getCurrentId();
|
||||
if (currentChannelId) {
|
||||
this.state = {currentChannelId: currentChannelId, postLists: [currentChannelId]};
|
||||
} else {
|
||||
this.state = {currentChannelId: null, postLists: []};
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
ChannelStore.addChangeListener(this.onChange);
|
||||
ChannelStore.addLeaveListener(this.onLeave);
|
||||
}
|
||||
onChange() {
|
||||
let channelId = ChannelStore.getCurrentId();
|
||||
if (channelId === this.state.currentChannelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let postLists = this.state.postLists;
|
||||
if (postLists.indexOf(channelId) === -1) {
|
||||
postLists.push(channelId);
|
||||
}
|
||||
this.setState({currentChannelId: channelId, postLists: postLists});
|
||||
}
|
||||
onLeave(id) {
|
||||
let postLists = this.state.postLists;
|
||||
var index = postLists.indexOf(id);
|
||||
if (index !== -1) {
|
||||
postLists.splice(index, 1);
|
||||
}
|
||||
}
|
||||
render() {
|
||||
let postLists = this.state.postLists;
|
||||
let channelId = this.state.currentChannelId;
|
||||
|
||||
let postListCtls = [];
|
||||
for (let i = 0; i <= this.state.postLists.length - 1; i++) {
|
||||
postListCtls.push(
|
||||
<PostList
|
||||
channelId={postLists[i]}
|
||||
isActive={postLists[i] === channelId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>{postListCtls}</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -12,14 +12,18 @@ export default class SettingItemMin extends React.Component {
|
||||
href='#'
|
||||
onClick={this.props.updateSection}
|
||||
>
|
||||
Edit
|
||||
<i className='fa fa-pencil'/>
|
||||
{'Edit'}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className='section-min'>
|
||||
<ul
|
||||
className='section-min'
|
||||
onClick={this.props.updateSection}
|
||||
>
|
||||
<li className='col-sm-10 section-title'>{this.props.title}</li>
|
||||
{editButton}
|
||||
<li className='col-sm-7 section-describe'>{this.props.describe}</li>
|
||||
|
||||
@@ -64,9 +64,9 @@ export default class SettingsUpload extends React.Component {
|
||||
}
|
||||
return (
|
||||
<ul className='section-max'>
|
||||
<li className='col-xs-12 section-title'>{this.props.title}</li>
|
||||
<li className='col-xs-offset-3'>{this.props.helpText}</li>
|
||||
<li className='col-xs-offset-3 col-xs-8'>
|
||||
<li className='col-sm-12 section-title'>{this.props.title}</li>
|
||||
<li className='col-sm-offset-3 col-sm-9'>{this.props.helpText}</li>
|
||||
<li className='col-sm-offset-3 col-sm-9'>
|
||||
<ul className='setting-list'>
|
||||
<li className='setting-list-item'>
|
||||
<span className='btn btn-sm btn-primary btn-file sel-btn'>
|
||||
|
||||
@@ -315,10 +315,12 @@ export default class Sidebar extends React.Component {
|
||||
if (unread) {
|
||||
titleClass = 'unread-title';
|
||||
|
||||
if (!this.firstUnreadChannel) {
|
||||
this.firstUnreadChannel = channel.name;
|
||||
if (channel.id !== activeId) {
|
||||
if (!this.firstUnreadChannel) {
|
||||
this.firstUnreadChannel = channel.name;
|
||||
}
|
||||
this.lastUnreadChannel = channel.name;
|
||||
}
|
||||
this.lastUnreadChannel = channel.name;
|
||||
}
|
||||
|
||||
var badge = null;
|
||||
|
||||
94
web/react/components/team_export_tab.jsx
Обычный файл
94
web/react/components/team_export_tab.jsx
Обычный файл
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var Client = require('../utils/client.jsx');
|
||||
|
||||
export default class TeamExportTab extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {status: 'request', link: '', err: ''};
|
||||
|
||||
this.onExportSuccess = this.onExportSuccess.bind(this);
|
||||
this.onExportFailure = this.onExportFailure.bind(this);
|
||||
this.doExport = this.doExport.bind(this);
|
||||
}
|
||||
onExportSuccess(data) {
|
||||
this.setState({status: 'ready', link: data.link, err: ''});
|
||||
}
|
||||
onExportFailure(e) {
|
||||
this.setState({status: 'failure', link: '', err: e.message});
|
||||
}
|
||||
doExport() {
|
||||
if (this.state.status === 'in-progress') {
|
||||
return;
|
||||
}
|
||||
this.setState({status: 'in-progress'});
|
||||
Client.exportTeam(this.onExportSuccess, this.onExportFailure);
|
||||
}
|
||||
render() {
|
||||
var messageSection = '';
|
||||
switch (this.state.status) {
|
||||
case 'request':
|
||||
messageSection = '';
|
||||
break;
|
||||
case 'in-progress':
|
||||
messageSection = (
|
||||
<p className='confirm-import alert alert-warning'>
|
||||
<i className='fa fa-spinner fa-pulse' />
|
||||
{' Exporting...'}
|
||||
</p>
|
||||
);
|
||||
break;
|
||||
case 'ready':
|
||||
messageSection = (
|
||||
<p className='confirm-import alert alert-success'>
|
||||
<i className='fa fa-check' />
|
||||
{' Ready for '}
|
||||
<a
|
||||
href={this.state.link}
|
||||
download={true}
|
||||
>
|
||||
{'download'}
|
||||
</a>
|
||||
</p>
|
||||
);
|
||||
break;
|
||||
case 'failure':
|
||||
messageSection = (
|
||||
<p className='confirm-import alert alert-warning'>
|
||||
<i className='fa fa-warning' />
|
||||
{' Unable to export: ' + this.state.err}
|
||||
</p>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref='wrapper'
|
||||
className='user-settings'
|
||||
>
|
||||
<h3 className='tab-header'>{'Export'}</h3>
|
||||
<div className='divider-dark first'/>
|
||||
<ul className='section-max'>
|
||||
<li className='col-xs-12 section-title'>{'Export your team'}</li>
|
||||
<li className='col-xs-offset-3 col-xs-8'>
|
||||
<ul className='setting-list'>
|
||||
<li className='setting-list-item'>
|
||||
<a
|
||||
className='btn btn-sm btn-primary btn-file sel-btn'
|
||||
href='#'
|
||||
onClick={this.doExport}
|
||||
>
|
||||
{'Export'}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div className='divider-dark'/>
|
||||
{messageSection}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -34,13 +34,11 @@ export default class TeamImportTab extends React.Component {
|
||||
render() {
|
||||
var uploadHelpText = (
|
||||
<div>
|
||||
<br/>
|
||||
Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team's public channels.
|
||||
<br/><br/>
|
||||
The Slack import to Mattermost is in "Preview". Slack bot posts and channels with underscores do not yet import.
|
||||
<br/><br/>
|
||||
<p>{'Slack does not allow you to export files, images, private groups or direct messages stored in Slack. Therefore, Slack import to Mattermost only supports importing of text messages in your Slack team\'\s public channels.'}</p>
|
||||
<p>{'The Slack import to Mattermost is in "Preview". Slack bot posts and channels with underscores do not yet import.'}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
var uploadSection = (
|
||||
<SettingUpload
|
||||
title='Import from Slack'
|
||||
@@ -58,7 +56,7 @@ export default class TeamImportTab extends React.Component {
|
||||
break;
|
||||
case 'in-progress':
|
||||
messageSection = (
|
||||
<p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i> Importing...</p>
|
||||
<p className='confirm-import alert alert-warning'><i className='fa fa-spinner fa-pulse'></i>{' Importing...'}</p>
|
||||
);
|
||||
break;
|
||||
case 'done':
|
||||
@@ -99,18 +97,18 @@ export default class TeamImportTab extends React.Component {
|
||||
data-dismiss='modal'
|
||||
aria-label='Close'
|
||||
>
|
||||
<span aria-hidden='true'>×</span>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
</button>
|
||||
<h4
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
><i className='modal-back'></i>Import</h4>
|
||||
><i className='modal-back'></i>{'Import'}</h4>
|
||||
</div>
|
||||
<div
|
||||
ref='wrapper'
|
||||
className='user-settings'
|
||||
>
|
||||
<h3 className='tab-header'>Import</h3>
|
||||
<h3 className='tab-header'>{'Import'}</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{uploadSection}
|
||||
<div className='divider-dark'/>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
var TeamStore = require('../stores/team_store.jsx');
|
||||
var ImportTab = require('./team_import_tab.jsx');
|
||||
var ExportTab = require('./team_export_tab.jsx');
|
||||
var FeatureTab = require('./team_feature_tab.jsx');
|
||||
var GeneralTab = require('./team_general_tab.jsx');
|
||||
var Utils = require('../utils/utils.jsx');
|
||||
@@ -64,6 +65,13 @@ export default class TeamSettings extends React.Component {
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'export':
|
||||
result = (
|
||||
<div>
|
||||
<ExportTab />
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
result = (
|
||||
<div/>
|
||||
|
||||
@@ -36,6 +36,7 @@ export default class TeamSettingsModal extends React.Component {
|
||||
let tabs = [];
|
||||
tabs.push({name: 'general', uiName: 'General', icon: 'glyphicon glyphicon-cog'});
|
||||
tabs.push({name: 'import', uiName: 'Import', icon: 'glyphicon glyphicon-upload'});
|
||||
tabs.push({name: 'export', uiName: 'Export', icon: 'glyphicon glyphicon-download'});
|
||||
tabs.push({name: 'feature', uiName: 'Advanced', icon: 'glyphicon glyphicon-wrench'});
|
||||
|
||||
return (
|
||||
|
||||
@@ -242,7 +242,7 @@ export default class Textbox extends React.Component {
|
||||
const e = React.findDOMNode(this.refs.message);
|
||||
const w = React.findDOMNode(this.refs.wrapper);
|
||||
|
||||
let prevHeight = $(e).height();
|
||||
const prevHeight = $(e).height();
|
||||
|
||||
const lht = parseInt($(e).css('lineHeight'), 10);
|
||||
const lines = e.scrollHeight / lht;
|
||||
@@ -260,7 +260,7 @@ export default class Textbox extends React.Component {
|
||||
$(w).css({height: 'auto'}).height(167);
|
||||
}
|
||||
|
||||
if (prevHeight !== $(e).height()) {
|
||||
if (prevHeight !== $(e).height() && this.props.onHeightChange) {
|
||||
this.props.onHeightChange();
|
||||
}
|
||||
}
|
||||
@@ -320,6 +320,6 @@ Textbox.propTypes = {
|
||||
messageText: React.PropTypes.string.isRequired,
|
||||
onUserInput: React.PropTypes.func.isRequired,
|
||||
onKeyPress: React.PropTypes.func.isRequired,
|
||||
onHeightChange: React.PropTypes.func.isRequired,
|
||||
onHeightChange: React.PropTypes.func,
|
||||
createMessage: React.PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
@@ -40,6 +40,7 @@ export default class UserSettings extends React.Component {
|
||||
user={this.state.user}
|
||||
activeSection={this.props.activeSection}
|
||||
updateSection={this.props.updateSection}
|
||||
updateTab={this.props.updateTab}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -86,4 +87,4 @@ UserSettings.propTypes = {
|
||||
activeSection: React.PropTypes.string,
|
||||
updateSection: React.PropTypes.func,
|
||||
updateTab: React.PropTypes.func
|
||||
};
|
||||
};
|
||||
|
||||
@@ -238,7 +238,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
key='firstNameSetting'
|
||||
className='form-group'
|
||||
>
|
||||
<label className='col-sm-5 control-label'>First Name</label>
|
||||
<label className='col-sm-5 control-label'>{'First Name'}</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -255,7 +255,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
key='lastNameSetting'
|
||||
className='form-group'
|
||||
>
|
||||
<label className='col-sm-5 control-label'>Last Name</label>
|
||||
<label className='col-sm-5 control-label'>{'Last Name'}</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -267,6 +267,28 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
</div>
|
||||
);
|
||||
|
||||
function notifClick(e) {
|
||||
e.preventDefault();
|
||||
this.updateSection('');
|
||||
this.props.updateTab('notifications');
|
||||
}
|
||||
|
||||
const notifLink = (
|
||||
<a
|
||||
href='#'
|
||||
onClick={notifClick.bind(this)}
|
||||
>
|
||||
{'Notifications'}
|
||||
</a>
|
||||
);
|
||||
|
||||
const extraInfo = (
|
||||
<span>
|
||||
{'By default, you will receive mention notifications when someone types your first name. '}
|
||||
{'Go to '} {notifLink} {'settings to change this default.'}
|
||||
</span>
|
||||
);
|
||||
|
||||
nameSection = (
|
||||
<SettingItemMax
|
||||
title='Full Name'
|
||||
@@ -278,6 +300,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
this.updateSection('');
|
||||
e.preventDefault();
|
||||
}.bind(this)}
|
||||
extraInfo={extraInfo}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@@ -326,6 +349,13 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
</div>
|
||||
);
|
||||
|
||||
const extraInfo = (
|
||||
<span>
|
||||
{'Use Nickname for a name you might be called that is different from your first name and user name.'}
|
||||
{'This is most often used when two or more people have similar sounding names and usernames.'}
|
||||
</span>
|
||||
);
|
||||
|
||||
nicknameSection = (
|
||||
<SettingItemMax
|
||||
title='Nickname'
|
||||
@@ -337,6 +367,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
this.updateSection('');
|
||||
e.preventDefault();
|
||||
}.bind(this)}
|
||||
extraInfo={extraInfo}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@@ -375,6 +406,8 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
</div>
|
||||
);
|
||||
|
||||
const extraInfo = (<span>{'Pick something easy for teammates to recognize and recall.'}</span>);
|
||||
|
||||
usernameSection = (
|
||||
<SettingItemMax
|
||||
title='Username'
|
||||
@@ -386,6 +419,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
this.updateSection('');
|
||||
e.preventDefault();
|
||||
}.bind(this)}
|
||||
extraInfo={extraInfo}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@@ -404,13 +438,13 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
let helpText = <div>Email is used for notifications, and requires verification if changed.</div>;
|
||||
|
||||
if (!this.state.emailEnabled) {
|
||||
helpText = <div className='text-danger'><br />Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.</div>;
|
||||
helpText = <div className='setting-list__hint text-danger'>{'Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'}</div>;
|
||||
}
|
||||
|
||||
inputs.push(
|
||||
<div key='emailSetting'>
|
||||
<div className='form-group'>
|
||||
<label className='col-sm-5 control-label'>Primary Email</label>
|
||||
<label className='col-sm-5 control-label'>{'Primary Email'}</label>
|
||||
<div className='col-sm-7'>
|
||||
<input
|
||||
className='form-control'
|
||||
@@ -492,18 +526,18 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
data-dismiss='modal'
|
||||
aria-label='Close'
|
||||
>
|
||||
<span aria-hidden='true'>×</span>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
</button>
|
||||
<h4
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i className='modal-back'></i>
|
||||
General Settings
|
||||
{'General Settings'}
|
||||
</h4>
|
||||
</div>
|
||||
<div className='user-settings'>
|
||||
<h3 className='tab-header'>General Settings</h3>
|
||||
<h3 className='tab-header'>{'General Settings'}</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{nameSection}
|
||||
<div className='divider-light'/>
|
||||
@@ -524,5 +558,6 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
UserSettingsGeneralTab.propTypes = {
|
||||
user: React.PropTypes.object,
|
||||
updateSection: React.PropTypes.func,
|
||||
updateTab: React.PropTypes.func,
|
||||
activeSection: React.PropTypes.string
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
|
||||
var Navbar = require('../components/navbar.jsx');
|
||||
var Sidebar = require('../components/sidebar.jsx');
|
||||
var ChannelHeader = require('../components/channel_header.jsx');
|
||||
var PostList = require('../components/post_list.jsx');
|
||||
var PostListContainer = require('../components/post_list_container.jsx');
|
||||
var CreatePost = require('../components/create_post.jsx');
|
||||
var SidebarRight = require('../components/sidebar_right.jsx');
|
||||
var SidebarRightMenu = require('../components/sidebar_right_menu.jsx');
|
||||
@@ -159,7 +159,7 @@ function setupChannelPage(teamName, teamType, teamId, channelName, channelId) {
|
||||
);
|
||||
|
||||
React.render(
|
||||
<PostList />,
|
||||
<PostListContainer />,
|
||||
document.getElementById('post-list')
|
||||
);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ var ActionTypes = Constants.ActionTypes;
|
||||
var BrowserStore = require('../stores/browser_store.jsx');
|
||||
|
||||
var CHANGE_EVENT = 'change';
|
||||
var LEAVE_EVENT = 'leave';
|
||||
var MORE_CHANGE_EVENT = 'change';
|
||||
var EXTRA_INFO_EVENT = 'extra_info';
|
||||
|
||||
@@ -48,6 +49,15 @@ class ChannelStoreClass extends EventEmitter {
|
||||
removeExtraInfoChangeListener(callback) {
|
||||
this.removeListener(EXTRA_INFO_EVENT, callback);
|
||||
}
|
||||
emitLeave(id) {
|
||||
this.emit(LEAVE_EVENT, id);
|
||||
}
|
||||
addLeaveListener(callback) {
|
||||
this.on(LEAVE_EVENT, callback);
|
||||
}
|
||||
removeLeaveListener(callback) {
|
||||
this.removeListener(LEAVE_EVENT, callback);
|
||||
}
|
||||
findFirstBy(field, value) {
|
||||
var channels = this.pGetChannels();
|
||||
for (var i = 0; i < channels.length; i++) {
|
||||
@@ -272,6 +282,10 @@ ChannelStore.dispatchToken = AppDispatcher.register(function handleAction(payloa
|
||||
ChannelStore.emitExtraInfoChange();
|
||||
break;
|
||||
|
||||
case ActionTypes.LEAVE_CHANNEL:
|
||||
ChannelStore.emitLeave(action.id);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -919,6 +919,19 @@ export function importSlack(fileData, success, error) {
|
||||
});
|
||||
}
|
||||
|
||||
export function exportTeam(success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/teams/export_team',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: success,
|
||||
error: function onError(xhr, status, err) {
|
||||
var e = handleError('exportTeam', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getStatuses(success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/users/status',
|
||||
|
||||
@@ -9,6 +9,7 @@ module.exports = {
|
||||
|
||||
CLICK_CHANNEL: null,
|
||||
CREATE_CHANNEL: null,
|
||||
LEAVE_CHANNEL: null,
|
||||
RECIEVED_CHANNELS: null,
|
||||
RECIEVED_CHANNEL: null,
|
||||
RECIEVED_MORE_CHANNELS: null,
|
||||
|
||||
@@ -498,7 +498,7 @@ export function textToJsx(textin, options) {
|
||||
|
||||
// do both a non-case sensitive and case senstive check
|
||||
let mClass = '';
|
||||
if (('@' + name.toLowerCase()) !== -1 || implicitKeywords.indexOf('@' + name) !== -1) {
|
||||
if (implicitKeywords.indexOf('@' + name.toLowerCase()) !== -1 || implicitKeywords.indexOf('@' + name) !== -1) {
|
||||
mClass = mentionClass;
|
||||
}
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user