Этот коммит содержится в:
Christopher Speller
2017-07-18 15:45:23 -07:00
родитель 21a3219b9b a350f4dc07
Коммит 97f34e483b
101 изменённых файлов: 3132 добавлений и 2069 удалений

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

@@ -215,6 +215,11 @@ export default class AddUsersToTeam extends React.Component {
/>
);
let users = [];
if (this.state.users) {
users = this.state.users.filter((user) => user.delete_at === 0);
}
return (
<Modal
dialogClassName={'more-modal more-direct-channels'}
@@ -238,7 +243,7 @@ export default class AddUsersToTeam extends React.Component {
<Modal.Body>
<MultiSelect
key='addUsersToTeamKey'
options={this.state.users}
options={users}
optionRenderer={this.renderOption}
values={this.state.values}
valueRenderer={this.renderValue}

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

@@ -127,14 +127,14 @@ export default class PushSettings extends AdminSettings {
pushServerHelpText = (
<FormattedHTMLMessage
id='admin.email.mhpnsHelp'
defaultMessage='Download <a href="https://itunes.apple.com/us/app/mattermost/id984966508?mt=8" target="_blank">Mattermost iOS app</a> from iTunes. Download <a href="https://play.google.com/store/apps/details?id=com.mattermost.mattermost&hl=en" target="_blank">Mattermost Android app</a> from Google Play. Learn more about the <a href="http://docs.mattermost.com/deployment/push.html#hosted-push-notifications-service-hpns" target="_blank">Mattermost Hosted Push Notification Service</a>.'
defaultMessage='Download <a href="https://about.mattermost.com/mattermost-ios-app/" target="_blank">Mattermost iOS app</a> from iTunes. Download <a href="https://about.mattermost.com/mattermost-android-app/" target="_blank">Mattermost Android app</a> from Google Play. Learn more about the <a href="http://docs.mattermost.com/deployment/push.html#hosted-push-notifications-service-hpns" target="_blank">Mattermost Hosted Push Notification Service</a>.'
/>
);
} else if (this.state.pushNotificationServerType === PUSH_NOTIFICATIONS_MTPNS) {
pushServerHelpText = (
<FormattedHTMLMessage
id='admin.email.mtpnsHelp'
defaultMessage='Download <a href="https://itunes.apple.com/us/app/mattermost/id984966508?mt=8" target="_blank">Mattermost iOS app</a> from iTunes. Download <a href="https://play.google.com/store/apps/details?id=com.mattermost.mattermost&hl=en" target="_blank">Mattermost Android app</a> from Google Play. Learn more about the <a href="http://docs.mattermost.com/deployment/push.html#test-push-notifications-service-tpns" target="_blank">Mattermost Test Push Notification Service</a>.'
defaultMessage='Download <a href="https://about.mattermost.com/mattermost-ios-app/" target="_blank">Mattermost iOS app</a> from iTunes. Download <a href="https://about.mattermost.com/mattermost-android-app/" target="_blank">Mattermost Android app</a> from Google Play. Learn more about the <a href="http://docs.mattermost.com/deployment/push.html#test-push-notifications-service-tpns" target="_blank">Mattermost Test Push Notification Service</a>.'
/>
);
} else {

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

@@ -77,15 +77,15 @@ export default class SamlSettings extends AdminSettings {
AdminActions.samlCertificateStatus(
(data) => {
const files = {};
if (!data.IdpCertificateFile) {
if (!data.idp_certificate_file) {
files.idpCertificateFile = '';
}
if (!data.PublicCertificateFile) {
if (!data.public_certificate_file) {
files.publicCertificateFile = '';
}
if (!data.PrivateKeyFile) {
if (!data.private_key_file) {
files.privateKeyFile = '';
}
this.setState(files);

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

@@ -964,7 +964,10 @@ export default class ChannelHeader extends React.Component {
placement='bottom'
overlay={recentMentionsTooltip}
>
<div className='channel-header__icon icon--hidden'>
<div
className='channel-header__icon icon--hidden'
onClick={this.searchMentions}
>
<span
className='icon icon__mentions'
dangerouslySetInnerHTML={{__html: mentionsIcon}}

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

@@ -143,13 +143,18 @@ export default class ChannelInviteModal extends React.Component {
inviteError = (<label className='has-error control-label'>{this.state.inviteError}</label>);
}
let users = [];
if (this.state.users) {
users = this.state.users.filter((user) => user.delete_at === 0);
}
let content;
if (this.state.loading) {
content = (<LoadingScreen/>);
} else {
content = (
<SearchableUserList
users={this.state.users}
users={users}
usersPerPage={USERS_PER_PAGE}
total={this.state.total}
nextPage={this.nextPage}

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

@@ -58,10 +58,12 @@ export default class DotMenu extends Component {
constructor(props) {
super(props);
this.handleDropdownOpened = this.handleDropdownOpened.bind(this);
this.canDelete = false;
this.canEdit = false;
this.editDisableAction = new DelayedAction(this.handleEditDisable);
this.state = {
canDelete: PostUtils.canDeletePost(props.post),
canEdit: PostUtils.canEditPost(props.post, this.editDisableAction)
};
}
componentDidMount() {
@@ -69,7 +71,11 @@ export default class DotMenu extends Component {
$('#' + this.props.idPrefix + '_dropdown' + this.props.post.id).on('hidden.bs.dropdown', () => this.props.handleDropdownOpened(false));
}
handleDropdownOpened() {
componentWillUnmount() {
this.editDisableAction.cancel();
}
handleDropdownOpened = () => {
this.props.handleDropdownOpened(true);
const position = $('#post-list').height() - $(this.refs.dropdownToggle).offset().top;
@@ -80,17 +86,15 @@ export default class DotMenu extends Component {
}
}
handleEditDisable() {
this.canEdit = false;
handleEditDisable = () => {
this.setState({canEdit: false});
}
render() {
const isSystemMessage = PostUtils.isSystemMessage(this.props.post);
const isMobile = Utils.isMobile();
this.canDelete = PostUtils.canDeletePost(this.props.post);
this.canEdit = PostUtils.canEditPost(this.props.post, this.editDisableAction);
if (this.props.idPrefix === Constants.CENTER && (!isMobile && isSystemMessage && !this.canDelete && !this.canEdit)) {
if (this.props.idPrefix === Constants.CENTER && (!isMobile && isSystemMessage && !this.state.canDelete && !this.state.canEdit)) {
return null;
}
@@ -157,7 +161,7 @@ export default class DotMenu extends Component {
}
let dotMenuDelete = null;
if (this.canDelete) {
if (this.state.canDelete) {
dotMenuDelete = (
<DotMenuItem
idPrefix={idPrefix + 'Delete'}
@@ -169,7 +173,7 @@ export default class DotMenu extends Component {
}
let dotMenuEdit = null;
if (this.canEdit) {
if (this.state.canEdit) {
dotMenuEdit = (
<DotMenuEdit
idPrefix={idPrefix + 'Edit'}

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

@@ -3,7 +3,8 @@
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {flagPost, unflagPost, pinPost, unpinPost} from 'mattermost-redux/actions/posts';
import {flagPost, unflagPost} from 'mattermost-redux/actions/posts';
import {pinPost, unpinPost} from 'actions/post_actions.jsx';
import DotMenu from './dot_menu.jsx';

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

@@ -309,7 +309,7 @@ export default class EmojiPicker extends React.Component {
right: this.props.rightOffset
};
} else {
pickerStyle = this.props.style;
pickerStyle = {...this.props.style};
}
}
@@ -325,91 +325,111 @@ export default class EmojiPicker extends React.Component {
<div className='emoji-picker__categories'>
<EmojiPickerCategory
category='recent'
icon={<i
className='fa fa-clock-o'
title={Utils.localizeMessage('emoji_picker.recent', 'Recently Used')}
/>}
icon={
<i
className='fa fa-clock-o'
title={Utils.localizeMessage('emoji_picker.recent', 'Recently Used')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'recent'}
/>
<EmojiPickerCategory
category='people'
icon={<i
className='fa fa-smile-o'
title={Utils.localizeMessage('emoji_picker.people', 'People')}
/>}
icon={
<i
className='fa fa-smile-o'
title={Utils.localizeMessage('emoji_picker.people', 'People')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'people'}
/>
<EmojiPickerCategory
category='nature'
icon={<i
className='fa fa-leaf'
title={Utils.localizeMessage('emoji_picker.nature', 'Nature')}
/>}
icon={
<i
className='fa fa-leaf'
title={Utils.localizeMessage('emoji_picker.nature', 'Nature')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'nature'}
/>
<EmojiPickerCategory
category='food'
icon={<i
className='fa fa-cutlery'
title={Utils.localizeMessage('emoji_picker.food', 'Food')}
/>}
icon={
<i
className='fa fa-cutlery'
title={Utils.localizeMessage('emoji_picker.food', 'Food')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'food'}
/>
<EmojiPickerCategory
category='activity'
icon={<i
className='fa fa-futbol-o'
title={Utils.localizeMessage('emoji_picker.activity', 'Activity')}
/>}
icon={
<i
className='fa fa-futbol-o'
title={Utils.localizeMessage('emoji_picker.activity', 'Activity')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'activity'}
/>
<EmojiPickerCategory
category='travel'
icon={<i
className='fa fa-plane'
title={Utils.localizeMessage('emoji_picker.travel', 'Travel')}
/>}
icon={
<i
className='fa fa-plane'
title={Utils.localizeMessage('emoji_picker.travel', 'Travel')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'travel'}
/>
<EmojiPickerCategory
category='objects'
icon={<i
className='fa fa-lightbulb-o'
title={Utils.localizeMessage('emoji_picker.objects', 'Objects')}
/>}
icon={
<i
className='fa fa-lightbulb-o'
title={Utils.localizeMessage('emoji_picker.objects', 'Objects')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'objects'}
/>
<EmojiPickerCategory
category='symbols'
icon={<i
className='fa fa-heart-o'
title={Utils.localizeMessage('emoji_picker.symbols', 'Symbols')}
/>}
icon={
<i
className='fa fa-heart-o'
title={Utils.localizeMessage('emoji_picker.symbols', 'Symbols')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'symbols'}
/>
<EmojiPickerCategory
category='flags'
icon={<i
className='fa fa-flag-o'
title={Utils.localizeMessage('emoji_picker.flags', 'Flags')}
/>}
icon={
<i
className='fa fa-flag-o'
title={Utils.localizeMessage('emoji_picker.flags', 'Flags')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'flags'}
/>
<EmojiPickerCategory
category='custom'
icon={<i
className='fa fa-at'
title={Utils.localizeMessage('emoji_picker.custom', 'Custom')}
/>}
icon={
<i
className='fa fa-at'
title={Utils.localizeMessage('emoji_picker.custom', 'Custom')}
/>
}
onCategoryClick={this.handleCategoryClick}
selected={this.state.category === 'custom'}
/>

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

@@ -15,7 +15,15 @@ export default class EmojiPickerOverlay extends React.PureComponent {
onEmojiClick: PropTypes.func.isRequired,
onHide: PropTypes.func.isRequired,
rightOffset: PropTypes.number,
topOffset: PropTypes.number
topOffset: PropTypes.number,
spaceRequiredAbove: PropTypes.number,
spaceRequiredBelow: PropTypes.number
}
// Reasonable defaults calculated from from the center channel
static defaultProps = {
spaceRequiredAbove: 422,
spaceRequiredBelow: 436
}
constructor(props) {
@@ -28,15 +36,12 @@ export default class EmojiPickerOverlay extends React.PureComponent {
componentWillUpdate(nextProps) {
if (nextProps.show && !this.props.show) {
const spaceRequiredAbove = 422;
const spaceRequiredBelow = 436;
const targetBounds = nextProps.target().getBoundingClientRect();
let placement;
if (targetBounds.top > spaceRequiredAbove) {
if (targetBounds.top > nextProps.spaceRequiredAbove) {
placement = 'top';
} else if (window.innerHeight - targetBounds.bottom > spaceRequiredBelow) {
} else if (window.innerHeight - targetBounds.bottom > nextProps.spaceRequiredBelow) {
placement = 'bottom';
} else {
placement = 'left';

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

@@ -31,13 +31,11 @@ export default class EditIncomingWebhook extends AbstractIncomingWebhook {
handleIntegrationChange() {
const teamId = TeamStore.getCurrentId();
this.setState({
hooks: IntegrationStore.getIncomingWebhooks(teamId),
loading: !IntegrationStore.hasReceivedIncomingWebhooks(teamId)
});
const hooks = IntegrationStore.getIncomingWebhooks(teamId);
const loading = !IntegrationStore.hasReceivedIncomingWebhooks(teamId);
if (!this.state.loading) {
this.originalIncomingHook = this.state.hooks.filter((hook) => hook.id === this.props.location.query.id)[0];
if (!loading) {
this.originalIncomingHook = hooks.filter((hook) => hook.id === this.props.location.query.id)[0];
this.setState({
displayName: this.originalIncomingHook.display_name,

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

@@ -61,6 +61,7 @@ class LeaveTeamModal extends React.Component {
}
GlobalActions.emitLeaveTeam();
GlobalActions.toggleSideBarRightMenuAction();
}
handleHide() {

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

@@ -1,19 +1,67 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import React from 'react';
import {postListScrollChange} from 'actions/global_actions.jsx';
export default class MarkdownImage extends PureComponent {
handleLoad = () => {
postListScrollChange();
const WAIT_FOR_HEIGHT_TIMEOUT = 100;
export default class MarkdownImage extends React.PureComponent {
static propTypes = {
/*
* The href of the image to be loaded
*/
href: PropTypes.string
}
constructor(props) {
super(props);
this.heightTimeout = 0;
}
componentDidMount() {
this.waitForHeight();
}
componentDidUpdate(prevProps) {
if (this.props.href !== prevProps.href) {
this.waitForHeight();
}
}
componentWillUnmount() {
this.stopWaitingForHeight();
}
waitForHeight = () => {
if (this.refs.image.height) {
setTimeout(postListScrollChange, 0);
this.heightTimeout = 0;
} else {
this.heightTimeout = setTimeout(this.waitForHeight, WAIT_FOR_HEIGHT_TIMEOUT);
}
}
stopWaitingForHeight = () => {
if (this.heightTimeout !== 0) {
clearTimeout(this.heightTimeout);
this.heightTimeout = 0;
}
}
render() {
const props = {...this.props};
props.onLoad = this.handleLoad;
return <img {...props}/>;
return (
<img
{...this.props}
ref='image'
onLoad={this.stopWaitingForHeight}
onError={this.stopWaitingForHeight}
/>
);
}
}

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

@@ -146,7 +146,7 @@ export default class MemberListChannel extends React.Component {
for (let i = 0; i < users.length; i++) {
const user = users[i];
if (teamMembers[user.id] && channelMembers[user.id]) {
if (teamMembers[user.id] && channelMembers[user.id] && user.delete_at === 0) {
usersToDisplay.push(user);
actionUserProps[user.id] = {
channel: this.props.channel,

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

@@ -292,6 +292,11 @@ export default class MoreDirectChannels extends React.Component {
/>
);
let users = [];
if (this.state.users) {
users = this.state.users.filter((user) => user.delete_at === 0);
}
return (
<Modal
dialogClassName={'more-modal more-direct-channels'}
@@ -310,7 +315,7 @@ export default class MoreDirectChannels extends React.Component {
<Modal.Body>
<MultiSelect
key='moreDirectChannelsList'
options={this.state.users}
options={users}
optionRenderer={this.renderOption}
values={this.state.values}
valueRenderer={this.renderValue}

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

@@ -698,7 +698,7 @@ export default class Navbar extends React.Component {
/>
</span>
<span
className='icon icon__menu'
className='icon icon__menu icon--sidebarHeaderTextColor'
dangerouslySetInnerHTML={{__html: menuIcon}}
aria-hidden='true'
/>

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

@@ -84,7 +84,7 @@ export default class PDFPreview extends React.Component {
success: false
});
PDFJS.getDocument(window.mm_config.SiteURL + props.fileUrl).then(this.onDocumentLoad);
PDFJS.getDocument(props.fileUrl).then(this.onDocumentLoad);
}
onDocumentLoad(pdf) {

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

@@ -16,7 +16,7 @@ function mapStateToProps(state, ownProps) {
let parentPostUser;
if (ownProps.post.root_id) {
parentPost = getPost(state, ownProps.post.root_id);
parentPostUser = getUser(state, parentPost.user_id);
parentPostUser = parentPost ? getUser(state, parentPost.user_id) : null;
}
return {

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

@@ -191,7 +191,7 @@ export default class PostBodyAdditionalContent extends React.PureComponent {
);
const contents = [message];
if (this.state.linkLoaded) {
if (this.state.linkLoaded || this.props.previewCollapsed.startsWith('true')) {
if (prependToggle) {
contents.unshift(toggle);
} else {

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

@@ -81,8 +81,7 @@ export default class PostInfo extends React.PureComponent {
this.state = {
showEmojiPicker: false,
reactionPickerOffset: 21,
canEdit: PostUtils.canEditPost(props.post, this.editDisableAction)
reactionPickerOffset: 21
};
}

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

@@ -104,12 +104,13 @@ export default class PostList extends React.PureComponent {
this.previousScrollTop = Number.MAX_SAFE_INTEGER;
this.previousScrollHeight = 0;
this.previousClientHeight = 0;
this.atBottom = false;
this.state = {
atEnd: false,
unViewedCount: 0,
isScrolling: false,
lastViewed: Number.MAX_SAFE_INTEGER
lastViewed: props.lastViewedAt
};
}
@@ -144,24 +145,20 @@ export default class PostList extends React.PureComponent {
this.hasScrolled = false;
this.hasScrolledToFocusedPost = false;
this.hasScrolledToNewMessageSeparator = false;
this.setState({atEnd: false});
this.atBottom = false;
this.setState({atEnd: false, lastViewed: nextProps.lastViewedAt});
if (nextChannel.id) {
this.loadPosts(nextChannel.id);
}
return;
}
if (!this.wasAtBottom() && this.props.posts !== nextProps.posts) {
const unViewedCount = nextProps.posts.reduce((count, post) => {
if (post.create_at > this.state.lastViewed &&
post.user_id !== nextProps.currentUserId &&
post.state !== Constants.POST_DELETED) {
return count + 1;
}
return count;
}, 0);
this.setState({unViewedCount});
const nextPosts = nextProps.posts || [];
const posts = this.props.posts || [];
const hasNewPosts = (posts.length === 0 && nextPosts.length > 0) || (posts.length > 0 && nextPosts.length > 0 && posts[0].id !== nextPosts[0].id);
if (!this.checkBottom() && hasNewPosts) {
this.setUnreadsBelow(nextPosts, nextProps.currentUserId);
}
}
}
@@ -184,6 +181,10 @@ export default class PostList extends React.PureComponent {
const posts = this.props.posts;
const postList = this.refs.postlist;
if (!postList) {
return;
}
// Scroll to focused post on first load
const focusedPost = this.refs[this.props.focusedPostId];
if (focusedPost && this.props.posts) {
@@ -203,9 +204,13 @@ export default class PostList extends React.PureComponent {
if (messageSeparator && !this.hasScrolledToNewMessageSeparator) {
const element = ReactDOM.findDOMNode(messageSeparator);
element.scrollIntoView();
if (!this.checkBottom()) {
this.setUnreadsBelow(posts, this.props.currentUserId);
}
return;
} else if (postList && !this.hasScrolledToNewMessageSeparator) {
postList.scrollTop = postList.scrollHeight;
this.atBottom = true;
return;
}
@@ -218,7 +223,7 @@ export default class PostList extends React.PureComponent {
const pendingPostId = posts[0].pending_post_id;
if (postId !== prevPostId && pendingPostId !== prevPostId) {
// If already scrolled to bottom
if (this.wasAtBottom()) {
if (this.atBottom) {
doScrollToBottom = true;
}
@@ -229,6 +234,7 @@ export default class PostList extends React.PureComponent {
}
if (doScrollToBottom) {
this.atBottom = true;
postList.scrollTop = postList.scrollHeight;
return;
}
@@ -240,26 +246,55 @@ export default class PostList extends React.PureComponent {
}
}
setUnreadsBelow = (posts, currentUserId) => {
const unViewedCount = posts.reduce((count, post) => {
if (post.create_at > this.state.lastViewed &&
post.user_id !== currentUserId &&
post.state !== Constants.POST_DELETED) {
return count + 1;
}
return count;
}, 0);
this.setState({unViewedCount});
}
handleScrollStop = () => {
this.setState({
isScrolling: false
});
}
wasAtBottom = () => {
return this.previousClientHeight + this.previousScrollTop >= this.previousScrollHeight - CLOSE_TO_BOTTOM_SCROLL_MARGIN;
checkBottom = () => {
if (!this.refs.postlist) {
return true;
}
// No scroll bar so we're at the bottom
if (this.refs.postlist.scrollHeight <= this.refs.postlist.clientHeight) {
return true;
}
return this.refs.postlist.clientHeight + this.refs.postlist.scrollTop >= this.refs.postlist.scrollHeight - CLOSE_TO_BOTTOM_SCROLL_MARGIN;
}
handleResize = (forceScrollToBottom) => {
const postList = this.refs.postlist;
const doScrollToBottom = this.wasAtBottom() || forceScrollToBottom;
const messageSeparator = this.refs.newMessageSeparator;
const doScrollToBottom = this.atBottom || forceScrollToBottom;
if (postList && doScrollToBottom) {
postList.scrollTop = postList.scrollHeight;
if (postList) {
if (doScrollToBottom) {
postList.scrollTop = postList.scrollHeight;
} else if (!this.hasScrolled && messageSeparator) {
const element = ReactDOM.findDOMNode(messageSeparator);
element.scrollIntoView();
}
this.previousScrollHeight = postList.scrollHeight;
this.previousScrollTop = postList.scrollTop;
this.previousClientHeight = postList.clientHeight;
this.atBottom = this.checkBottom();
}
}
@@ -296,9 +331,18 @@ export default class PostList extends React.PureComponent {
}
handleScroll = () => {
this.hasScrolled = true;
// Only count as user scroll if we've already performed our first load scroll
this.hasScrolled = this.hasScrolledToNewMessageSeparator || this.hasScrolledToFocusedPost;
if (!this.refs.postlist) {
return;
}
this.previousScrollTop = this.refs.postlist.scrollTop;
if (this.refs.postlist.scrollHeight === this.previousScrollHeight) {
this.atBottom = this.checkBottom();
}
this.updateFloatingTimestamp();
if (!this.state.isScrolling) {
@@ -307,7 +351,7 @@ export default class PostList extends React.PureComponent {
});
}
if (this.wasAtBottom()) {
if (this.checkBottom()) {
this.setState({
lastViewed: new Date().getTime(),
unViewedCount: 0,
@@ -509,7 +553,7 @@ export default class PostList extends React.PureComponent {
/>
<ScrollToBottomArrows
isScrolling={this.state.isScrolling}
atBottom={this.wasAtBottom()}
atBottom={this.atBottom}
onClick={this.scrollToBottom}
/>
<NewMessageIndicator

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

@@ -305,7 +305,7 @@ export default class QuickSwitchModal extends React.PureComponent {
<Modal.Header closeButton={true}/>
<Modal.Body>
{header}
<div className='modal__hint hidden-xs'>
<div className='modal__hint'>
{help}
</div>
<SuggestionBox

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

@@ -11,7 +11,6 @@ import FailedPostOptions from 'components/post_view/failed_post_options';
import DotMenu from 'components/dot_menu';
import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import {addReaction} from 'actions/post_actions.jsx';
import TeamStore from 'stores/team_store.jsx';
@@ -27,6 +26,19 @@ import {Link} from 'react-router/es6';
import {FormattedMessage} from 'react-intl';
export default class RhsComment extends React.Component {
static propTypes = {
post: PropTypes.object,
lastPostCount: PropTypes.number,
user: PropTypes.object.isRequired,
currentUser: PropTypes.object.isRequired,
compactDisplay: PropTypes.bool,
useMilitaryTime: PropTypes.bool.isRequired,
isFlagged: PropTypes.bool,
status: PropTypes.string,
isBusy: PropTypes.bool,
removePost: PropTypes.func.isRequired
};
constructor(props) {
super(props);
@@ -56,7 +68,7 @@ export default class RhsComment extends React.Component {
}
removePost() {
GlobalActions.emitRemovePost(this.props.post);
this.props.removePost(this.props.post);
}
createRemovePostButton() {
@@ -333,9 +345,10 @@ export default class RhsComment extends React.Component {
show={this.state.showEmojiPicker}
onHide={this.toggleEmojiPicker}
target={() => this.refs.dotMenu}
container={this.props.getPostList}
onEmojiClick={this.reactEmojiClick}
rightOffset={15}
spaceRequiredAbove={342}
spaceRequiredBelow={342}
/>
<a
href='#'
@@ -436,16 +449,3 @@ export default class RhsComment extends React.Component {
);
}
}
RhsComment.propTypes = {
post: PropTypes.object,
lastPostCount: PropTypes.number,
user: PropTypes.object.isRequired,
currentUser: PropTypes.object.isRequired,
compactDisplay: PropTypes.bool,
useMilitaryTime: PropTypes.bool.isRequired,
isFlagged: PropTypes.bool,
status: PropTypes.string,
isBusy: PropTypes.bool,
getPostList: PropTypes.func.isRequired
};

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

@@ -38,8 +38,7 @@ export default class RhsRootPost extends React.Component {
isFlagged: PropTypes.bool,
status: PropTypes.string,
previewCollapsed: PropTypes.string,
isBusy: PropTypes.bool,
getPostList: PropTypes.func.isRequired
isBusy: PropTypes.bool
}
static defaultProps = {
@@ -226,9 +225,10 @@ export default class RhsRootPost extends React.Component {
show={this.state.showEmojiPicker}
onHide={this.toggleEmojiPicker}
target={() => this.refs.dotMenu}
container={this.props.getPostList}
onEmojiClick={this.reactEmojiClick}
rightOffset={15}
spaceRequiredAbove={342}
spaceRequiredBelow={342}
/>
<a
href='#'

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

@@ -2,7 +2,9 @@
// See License.txt for license information.
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {getPost, makeGetPostsForThread} from 'mattermost-redux/selectors/entities/posts';
import {removePost} from 'mattermost-redux/actions/posts';
import RhsThread from './rhs_thread.jsx';
@@ -24,4 +26,12 @@ function makeMapStateToProps() {
};
}
export default connect(makeMapStateToProps)(RhsThread);
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
removePost
}, dispatch)
};
}
export default connect(makeMapStateToProps, mapDispatchToProps)(RhsThread);

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

@@ -59,7 +59,10 @@ export default class RhsThread extends React.Component {
currentUser: PropTypes.object.isRequired,
useMilitaryTime: PropTypes.bool.isRequired,
toggleSize: PropTypes.func,
shrink: PropTypes.func
shrink: PropTypes.func,
actions: PropTypes.shape({
removePost: PropTypes.func.isRequired
}).isRequired
}
static defaultProps = {
@@ -316,10 +319,6 @@ export default class RhsThread extends React.Component {
});
}
getPostListContainer = () => {
return this.refs.postListContainer;
}
getSidebarBody = () => {
return this.refs.sidebarbody;
}
@@ -400,7 +399,7 @@ export default class RhsThread extends React.Component {
isFlagged={isFlagged}
status={status}
isBusy={this.state.isBusy}
getPostList={this.getPostListContainer}
removePost={this.props.actions.removePost}
/>
</div>
);
@@ -435,10 +434,7 @@ export default class RhsThread extends React.Component {
renderView={renderView}
onScroll={this.handleScroll}
>
<div
ref='postListContainer'
className='post-right__scroll'
>
<div className='post-right__scroll'>
<DateSeparator
date={rootPostDay}
/>
@@ -454,7 +450,6 @@ export default class RhsThread extends React.Component {
status={rootStatus}
previewCollapsed={this.state.previewsCollapsed}
isBusy={this.state.isBusy}
getPostList={this.getPostListContainer}
/>
<div
ref='rhspostlist'

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

@@ -21,7 +21,6 @@ import * as PostUtils from 'utils/post_utils.jsx';
import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
import {Posts} from 'mattermost-redux/constants';
import React from 'react';
import PropTypes from 'prop-types';
@@ -187,7 +186,7 @@ export default class SearchResultsItem extends React.Component {
let message;
let flagContent;
let rhsControls;
if (post.state === Posts.POST_DELETED) {
if (post.state === Constants.POST_DELETED) {
message = (
<p>
<FormattedMessage

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

@@ -77,12 +77,12 @@ export default class SidebarRight extends React.Component {
}
shouldComponentUpdate(nextProps, nextState) {
return !Utils.areObjectsEqual(nextState, this.state) || !Utils.areObjectsEqual(nextProps, this.props);
return !Utils.areObjectsEqual(nextState, this.state) || this.props.postRightVisible !== nextProps.postRightVisible;
}
componentWillUpdate(nextProps, nextState) {
const isOpen = this.state.searchVisible || this.props.postRightVisible;
const willOpen = nextState.searchVisible || nextState.postRightVisible;
const willOpen = nextState.searchVisible || nextProps.postRightVisible;
if (!isOpen && willOpen) {
trackEvent('ui', 'ui_rhs_opened');

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

@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import TeamMembersModal from './team_members_modal.jsx';
import ToggleModalButton from './toggle_modal_button.jsx';
import AboutBuildModal from './about_build_modal.jsx';
@@ -19,7 +18,6 @@ import * as UserAgent from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';
import {Constants, WebrtcActionTypes} from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
const Preferences = Constants.Preferences;
const TutorialSteps = Constants.TutorialSteps;
@@ -143,20 +141,7 @@ export default class SidebarRightMenu extends React.Component {
hideSidebars() {
if (Utils.isMobile()) {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_SEARCH,
results: null
});
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_POST_SELECTED,
postId: null
});
document.querySelector('.app__body .inner-wrap').classList.remove('move--right', 'move--left', 'move--left-small');
document.querySelector('.app__body .sidebar--left').classList.remove('move--right');
document.querySelector('.app__body .sidebar--right').classList.remove('move--left');
document.querySelector('.app__body .sidebar--menu').classList.remove('move--left');
GlobalActions.toggleSideBarRightMenuAction();
}
}

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

@@ -16,6 +16,12 @@ export default class SpinnerButton extends React.Component {
};
}
static get defaultProps() {
return {
spinning: false
};
}
render() {
const {spinning, children, ...props} = this.props; // eslint-disable-line no-use-before-define

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

@@ -29,12 +29,12 @@ class TeamImportTab extends React.Component {
};
}
onImportFailure(e, err, res) {
this.setState({status: 'fail', link: 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(res.text)});
onImportFailure() {
this.setState({status: 'fail'});
}
onImportSuccess(data, res) {
this.setState({status: 'done', link: 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(res.text)});
onImportSuccess(data) {
this.setState({status: 'done', link: 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(atob(data.results))});
}
doImportSlack(file) {

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

@@ -56,10 +56,6 @@ export default class UserProfile extends React.Component {
render() {
let name = '...';
let profileImg = '';
let popoverPosition = 'right';
if (Utils.isMobile()) {
popoverPosition = 'bottom';
}
if (this.props.user) {
name = Utils.displayUsername(this.props.user.id);
@@ -78,7 +74,7 @@ export default class UserProfile extends React.Component {
<OverlayTrigger
ref='overlay'
trigger='click'
placement={popoverPosition}
placement='right'
rootClose={true}
overlay={
<ProfilePopover