diff --git a/components/channel_members_modal/channel_members_modal.jsx b/components/channel_members_modal/channel_members_modal.jsx
index cac5f74a0205..58eda4c17c78 100644
--- a/components/channel_members_modal/channel_members_modal.jsx
+++ b/components/channel_members_modal/channel_members_modal.jsx
@@ -7,16 +7,30 @@ import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
import MemberListChannel from 'components/member_list_channel';
+import ChannelInviteModal from 'components/channel_invite_modal';
+import {ModalIdentifiers} from 'utils/constants';
export default class ChannelMembersModal extends React.PureComponent {
static propTypes = {
+
+ /**
+ * Bool whether user has permission to manage current channel
+ */
canManageChannelMembers: PropTypes.bool.isRequired,
- channel: PropTypes.shape({
- display_name: PropTypes.string.isRequired,
- delete_at: PropTypes.number.isRequired,
+
+ /**
+ * Object with info about current channel
+ */
+ channel: PropTypes.object.isRequired,
+
+ /**
+ * Function that is called when modal is hidden
+ */
+ onHide: PropTypes.func.isRequired,
+
+ actions: PropTypes.shape({
+ openModal: PropTypes.func.isRequired,
}).isRequired,
- onModalDismissed: PropTypes.func.isRequired,
- showInviteModal: PropTypes.func.isRequired,
}
constructor(props) {
@@ -27,15 +41,26 @@ export default class ChannelMembersModal extends React.PureComponent {
};
}
- onHide = () => {
+ handleHide = () => {
this.setState({
show: false,
});
}
- onClickManageChannelsButton = () => {
- this.props.showInviteModal();
- this.onHide();
+ handleExit = () => {
+ this.props.onHide();
+ }
+
+ onAddNewMembersButton = () => {
+ const {channel, actions} = this.props;
+
+ actions.openModal({
+ modalId: ModalIdentifiers.CHANNEL_INVITE,
+ dialogType: ChannelInviteModal,
+ dialogProps: {channel},
+ });
+
+ this.handleExit();
}
render() {
@@ -44,9 +69,9 @@ export default class ChannelMembersModal extends React.PureComponent {
@@ -61,7 +86,7 @@ export default class ChannelMembersModal extends React.PureComponent {
id='showInviteModal'
className='btn btn-md btn-primary'
href='#'
- onClick={this.onClickManageChannelsButton}
+ onClick={this.onAddNewMembersButton}
>
{
const baseProps = {
- channel: {id: 'channel_id', display_name: 'channel_display_name', delete_at: 0},
+ channel: {
+ id: 'channel_id',
+ display_name: 'channel_display_name',
+ delete_at: 0,
+ },
canManageChannelMembers: true,
- onModalDismissed: () => { }, //eslint-disable-line no-empty-function
- showInviteModal: () => { }, //eslint-disable-line no-empty-function
+ onHide: jest.fn(),
+ actions: {
+ openModal: jest.fn(),
+ },
};
test('should match snapshot', () => {
@@ -29,21 +38,27 @@ describe('components/ChannelMembersModal', () => {
);
wrapper.setState({show: true});
- wrapper.instance().onHide();
+ wrapper.instance().handleHide();
expect(wrapper.state('show')).toEqual(false);
});
- test('should have called props.showInviteModal and match state when onClickManageChannelsButton is called', () => {
- const showInviteModal = jest.fn();
- const props = {...baseProps, showInviteModal};
+ test('should have called props.actions.openModal and props.onHide when onAddNewMembersButton is called', () => {
+ const onHide = jest.fn();
+ const openModal = jest.fn();
+ const props = {
+ ...baseProps,
+ onHide,
+ actions: {
+ openModal,
+ },
+ };
const wrapper = shallow(
);
- wrapper.setState({show: true});
- wrapper.instance().onClickManageChannelsButton();
- expect(showInviteModal).toHaveBeenCalledTimes(1);
- expect(wrapper.state('show')).toEqual(false);
+ wrapper.instance().onAddNewMembersButton();
+ expect(openModal).toHaveBeenCalledTimes(1);
+ expect(onHide).toHaveBeenCalledTimes(1);
});
test('should have state when Modal.onHide', () => {
@@ -56,17 +71,6 @@ describe('components/ChannelMembersModal', () => {
expect(wrapper.state('show')).toEqual(false);
});
- test('should have called props.onModalDismissed when Modal.onExited', () => {
- const onModalDismissed = jest.fn();
- const props = {...baseProps, onModalDismissed};
- const wrapper = shallow(
-
- );
-
- wrapper.find(Modal).first().props().onExited();
- expect(onModalDismissed).toHaveBeenCalledTimes(1);
- });
-
test('should match snapshot with archived channel', () => {
const props = {...baseProps, channel: {...baseProps.channel, delete_at: 1234}};
@@ -76,21 +80,6 @@ describe('components/ChannelMembersModal', () => {
expect(wrapper).toMatchSnapshot();
});
-});
-
-describe('components/ChannelMembersModal', () => {
- const baseProps = {
- channel: {
- display_name: 'testchannel',
- header: '',
- name: 'testchannel',
- purpose: '',
- delete_at: 0,
- },
- canManageChannelMembers: true,
- onModalDismissed: () => {}, // eslint-disable-line no-empty-function
- showInviteModal: () => {}, // eslint-disable-line no-empty-function
- };
test('renders the channel display name', () => {
const wrapper = shallow(
@@ -99,17 +88,6 @@ describe('components/ChannelMembersModal', () => {
expect(wrapper.find('.name').text()).toBe(baseProps.channel.display_name);
});
- test('should call the onHide callback when the modal is hidden', () => {
- const onModalDismissed = jest.fn();
- const newProps = {...baseProps, onModalDismissed};
- const wrapper = shallow(
-
- );
- expect(onModalDismissed).not.toHaveBeenCalled();
- wrapper.find(Modal).first().props().onExited();
- expect(onModalDismissed).toHaveBeenCalled();
- });
-
test('should show the invite modal link if the user can manage channel members', () => {
const newProps = {...baseProps, canManageChannelMembers: true};
const wrapper = shallow(
@@ -126,14 +104,24 @@ describe('components/ChannelMembersModal', () => {
expect(wrapper.find('#showInviteModal').length).toBe(0);
});
- test('should call showInviteModal when the invite modal link is clicked', () => {
- const showInviteModal = jest.fn();
- const newProps = {...baseProps, canManageChannelMembers: false, showInviteModal};
+ test('should call openModal with ChannelInviteModal when the add members link is clicked', () => {
+ const openModal = jest.fn();
+ const newProps = {
+ ...baseProps,
+ canManageChannelMembers: false,
+ actions: {
+ openModal,
+ },
+ };
const wrapper = shallow(
);
- expect(showInviteModal).not.toHaveBeenCalled();
- wrapper.instance().onClickManageChannelsButton();
- expect(showInviteModal).toHaveBeenCalled();
+ expect(openModal).not.toHaveBeenCalled();
+ wrapper.instance().onAddNewMembersButton();
+ expect(openModal).toHaveBeenCalledWith({
+ modalId: ModalIdentifiers.CHANNEL_INVITE,
+ dialogType: ChannelInviteModal,
+ dialogProps: {channel: newProps.channel},
+ });
});
});
diff --git a/components/channel_members_modal/index.js b/components/channel_members_modal/index.js
index 754cad8490fc..e98f30ebad34 100644
--- a/components/channel_members_modal/index.js
+++ b/components/channel_members_modal/index.js
@@ -1,15 +1,20 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {canManageChannelMembers} from 'mattermost-redux/selectors/entities/channels';
-import ChannelMembersModal from './channel_members_modal.jsx';
+import {openModal} from 'actions/views/modals';
-function mapStateToProps(state) {
- return {
- canManageChannelMembers: canManageChannelMembers(state),
- };
-}
+import ChannelMembersModal from './channel_members_modal';
-export default connect(mapStateToProps)(ChannelMembersModal);
+const mapStateToProps = (state) => ({
+ canManageChannelMembers: canManageChannelMembers(state),
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ actions: bindActionCreators({openModal}, dispatch),
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(ChannelMembersModal);
diff --git a/components/channel_notifications_modal/channel_notifications_modal.jsx b/components/channel_notifications_modal/channel_notifications_modal.jsx
index 9ed621379d9c..139c810e3a37 100644
--- a/components/channel_notifications_modal/channel_notifications_modal.jsx
+++ b/components/channel_notifications_modal/channel_notifications_modal.jsx
@@ -14,15 +14,42 @@ import * as Utils from 'utils/utils.jsx';
import NotificationSection from 'components/channel_notifications_modal/components/notification_section.jsx';
-export default class ChannelNotificationsModal extends React.Component {
+export default class ChannelNotificationsModal extends React.PureComponent {
static propTypes = {
- show: PropTypes.bool.isRequired,
+
+ /**
+ * Function that is called when modal is hidden
+ */
onHide: PropTypes.func.isRequired,
+
+ /**
+ * Object with info about current channel
+ */
channel: PropTypes.object.isRequired,
+
+ /**
+ * Object with info about current channel membership
+ */
channelMember: PropTypes.object.isRequired,
+
+ /**
+ * Object with info about current user
+ */
currentUser: PropTypes.object.isRequired,
+
+ /**
+ * Boolean whether server sends push notifications
+ */
sendPushNotifications: PropTypes.bool.isRequired,
+
+ /*
+ * Object with redux action creators
+ */
actions: PropTypes.shape({
+
+ /*
+ * Action creator to update channel notify props
+ */
updateChannelNotifyProps: PropTypes.func.isRequired,
}),
};
@@ -31,6 +58,7 @@ export default class ChannelNotificationsModal extends React.Component {
super(props);
this.state = {
+ show: true,
activeSection: NotificationSections.NONE,
serverError: null,
...this.getStateFromNotifyProps(props.channelMember.notify_props),
@@ -55,9 +83,14 @@ export default class ChannelNotificationsModal extends React.Component {
};
}
- handleOnHide = () => {
- this.updateSection(NotificationSections.NONE);
+ handleHide = () => {
+ this.setState({
+ show: false,
+ });
+ }
+ handleExit = () => {
+ this.updateSection(NotificationSections.NONE);
this.props.onHide();
}
@@ -152,7 +185,6 @@ export default class ChannelNotificationsModal extends React.Component {
channelMember,
currentUser,
sendPushNotifications,
- show,
} = this.props;
let serverErrorTag = null;
@@ -162,10 +194,10 @@ export default class ChannelNotificationsModal extends React.Component {
return (
diff --git a/components/channel_notifications_modal/channel_notifications_modal.test.jsx b/components/channel_notifications_modal/channel_notifications_modal.test.jsx
index 23b7c1e508af..64e7f6addd17 100644
--- a/components/channel_notifications_modal/channel_notifications_modal.test.jsx
+++ b/components/channel_notifications_modal/channel_notifications_modal.test.jsx
@@ -60,19 +60,19 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
);
wrapper.setState({activeSection: NotificationSections.DESKTOP, desktopNotifyLevel: NotificationLevels.NONE});
- wrapper.instance().handleOnHide();
+ wrapper.instance().handleExit();
expect(onHide).toHaveBeenCalledTimes(1);
expect(wrapper.state('activeSection')).toEqual(NotificationSections.NONE);
expect(wrapper.state('desktopNotifyLevel')).toEqual(NotificationLevels.ALL);
wrapper.setState({activeSection: NotificationSections.MARK_UNREAD, markUnreadNotifyLevel: NotificationLevels.NONE});
- wrapper.instance().handleOnHide();
+ wrapper.instance().handleExit();
expect(onHide).toHaveBeenCalledTimes(2);
expect(wrapper.state('activeSection')).toEqual(NotificationSections.NONE);
expect(wrapper.state('markUnreadNotifyLevel')).toEqual(NotificationLevels.ALL);
wrapper.setState({activeSection: NotificationSections.PUSH, pushNotifyLevel: NotificationLevels.NONE});
- wrapper.instance().handleOnHide();
+ wrapper.instance().handleExit();
expect(onHide).toHaveBeenCalledTimes(3);
expect(wrapper.state('activeSection')).toEqual(NotificationSections.NONE);
expect(wrapper.state('pushNotifyLevel')).toEqual(NotificationLevels.DEFAULT);
diff --git a/components/channel_notifications_modal/index.js b/components/channel_notifications_modal/index.js
index 5ed7ee618109..8970f7a0abbb 100644
--- a/components/channel_notifications_modal/index.js
+++ b/components/channel_notifications_modal/index.js
@@ -1,29 +1,23 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
-
+import {connect} from 'react-redux';
import {updateChannelNotifyProps} from 'mattermost-redux/actions/channels';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
+import {getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
import ChannelNotificationsModal from './channel_notifications_modal.jsx';
-function mapStateToProps(state) {
- const config = getConfig(state);
- const sendPushNotifications = config.SendPushNotifications === 'true';
-
- return {
- sendPushNotifications,
- };
-}
+const mapStateToProps = (state) => ({
+ channelMember: getMyCurrentChannelMembership(state),
+ sendPushNotifications: getConfig(state).SendPushNotifications === 'true',
+});
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- updateChannelNotifyProps,
- }, dispatch),
- };
-}
+const mapDispatchToProps = (dispatch) => ({
+ actions: bindActionCreators({
+ updateChannelNotifyProps,
+ }, dispatch),
+});
export default connect(mapStateToProps, mapDispatchToProps)(ChannelNotificationsModal);
diff --git a/components/channel_view/index.js b/components/channel_view/index.js
index ed98b4b1bff2..4b5155c07089 100644
--- a/components/channel_view/index.js
+++ b/components/channel_view/index.js
@@ -5,6 +5,7 @@ import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {createSelector} from 'reselect';
import {getInt} from 'mattermost-redux/selectors/entities/preferences';
+import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {withRouter} from 'react-router-dom';
@@ -27,7 +28,7 @@ const getDeactivatedChannel = createSelector(
);
function mapStateToProps(state) {
- const channel = state.entities.channels.channels[state.entities.channels.currentChannelId];
+ const channel = getCurrentChannel(state);
const config = getConfig(state);
const enableTutorial = config.EnableTutorial === 'true';
diff --git a/components/create_comment/__snapshots__/create_comment.test.jsx.snap b/components/create_comment/__snapshots__/create_comment.test.jsx.snap
index a9149eb5e2c9..ae2d44ca37eb 100644
--- a/components/create_comment/__snapshots__/create_comment.test.jsx.snap
+++ b/components/create_comment/__snapshots__/create_comment.test.jsx.snap
@@ -134,6 +134,7 @@ exports[`components/CreateComment should match snapshot, comment with message 1`
onFileUpload={[Function]}
onFileUploadChange={[Function]}
onUploadError={[Function]}
+ onUploadProgress={[Function]}
onUploadStart={[Function]}
postType="comment"
rootId=""
@@ -261,6 +262,7 @@ exports[`components/CreateComment should match snapshot, emoji picker disabled 1
onFileUpload={[Function]}
onFileUploadChange={[Function]}
onUploadError={[Function]}
+ onUploadProgress={[Function]}
onUploadStart={[Function]}
postType="comment"
rootId=""
@@ -291,6 +293,7 @@ exports[`components/CreateComment should match snapshot, emoji picker disabled 1
}
onRemove={[Function]}
uploadsInProgress={Array []}
+ uploadsProgressPercent={Object {}}
/>
@@ -375,6 +378,7 @@ exports[`components/CreateComment should match snapshot, empty comment 1`] = `
onFileUpload={[Function]}
onFileUploadChange={[Function]}
onUploadError={[Function]}
+ onUploadProgress={[Function]}
onUploadStart={[Function]}
postType="comment"
rootId=""
@@ -502,6 +506,7 @@ exports[`components/CreateComment should match snapshot, non-empty message and u
onFileUpload={[Function]}
onFileUploadChange={[Function]}
onUploadError={[Function]}
+ onUploadProgress={[Function]}
onUploadStart={[Function]}
postType="comment"
rootId=""
@@ -569,6 +574,7 @@ exports[`components/CreateComment should match snapshot, non-empty message and u
Object {},
]
}
+ uploadsProgressPercent={Object {}}
/>
diff --git a/components/create_comment/create_comment.jsx b/components/create_comment/create_comment.jsx
index a2b1d2fe64f3..162f8bfb9b2d 100644
--- a/components/create_comment/create_comment.jsx
+++ b/components/create_comment/create_comment.jsx
@@ -17,7 +17,7 @@ import {containsAtChannel, postMessageOnKeyPress, shouldFocusMainTextbox} from '
import ConfirmModal from 'components/confirm_modal.jsx';
import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx';
-import FilePreview from 'components/file_preview.jsx';
+import FilePreview from 'components/file_preview/file_preview.jsx';
import FileUpload from 'components/file_upload';
import MsgTyping from 'components/msg_typing';
import PostDeletedModal from 'components/post_deleted_modal.jsx';
@@ -193,6 +193,7 @@ export default class CreateComment extends React.PureComponent {
fileInfos: [],
},
channelMembersCount: 0,
+ uploadsProgressPercent: {},
};
this.lastBlurAt = 0;
@@ -536,6 +537,11 @@ export default class CreateComment extends React.PureComponent {
this.focusTextbox();
}
+ handleUploadProgress = ({clientId, name, percent, type}) => {
+ const uploadsProgressPercent = {...this.state.uploadsProgressPercent, [clientId]: {percent, name, type}};
+ this.setState({uploadsProgressPercent});
+ }
+
handleFileUploadComplete = (fileInfos, clientIds, channelId, rootId) => {
const draft = this.draftsForPost[rootId];
const uploadsInProgress = [...draft.uploadsInProgress];
@@ -735,6 +741,7 @@ export default class CreateComment extends React.PureComponent {
fileInfos={draft.fileInfos}
onRemove={this.removePreview}
uploadsInProgress={draft.uploadsInProgress}
+ uploadsProgressPercent={this.state.uploadsProgressPercent}
ref='preview'
/>
);
@@ -775,6 +782,7 @@ export default class CreateComment extends React.PureComponent {
onUploadStart={this.handleUploadStart}
onFileUpload={this.handleFileUploadComplete}
onUploadError={this.handleUploadError}
+ onUploadProgress={this.handleUploadProgress}
rootId={this.props.rootId}
postType='comment'
/>
diff --git a/components/create_comment/create_comment.test.jsx b/components/create_comment/create_comment.test.jsx
index 8c4a835a1c48..99b371ed34a5 100644
--- a/components/create_comment/create_comment.test.jsx
+++ b/components/create_comment/create_comment.test.jsx
@@ -7,6 +7,8 @@ import {shallow} from 'enzyme';
import Constants from 'utils/constants.jsx';
import CreateComment from 'components/create_comment/create_comment.jsx';
+import FileUpload from 'components/file_upload';
+import FilePreview from 'components/file_preview/file_preview.jsx';
jest.mock('stores/post_store.jsx', () => ({
clearCommentDraftUploads: jest.fn(),
@@ -315,6 +317,17 @@ describe('components/CreateComment', () => {
expect(wrapper.state().draft.fileInfos).toEqual(expectedNewFileInfos);
});
+ it('check for uploadsProgressPercent state on handleUploadProgress callback', () => {
+ const wrapper = shallow(
+