Commit a0b36ae4 authored by Hyeseong Kim's avatar Hyeseong Kim Committed by Joram Wilander
Browse files

Refactor Modals, ChannelHeader and Navbar component (#1666)

* Refactor duplicated RenameChannelModal component

Remove duplicated RenameChannelModal code from Navbar and ChannelHeader, Use ModalController instead

* Refactor duplicated EditChannelPurposeModal comp

- Remove duplicated EditChannelPurposeModal code from ChannelHeader and
Navbar
- Use ModalController instead of to use it directly
- Change the modal to PureComponent

* Refactor duplicated ChannelMembersModal component

- Remove ChannelMembersModal code from ChannelHeader and Navbar
- Now the modal is managed by the ModalController instead of directly by the component.
- Add docs for props of ChannelMemebersModal

* Refactor ChannelNotificationsModal comp

- Change the ChannelNotificationsModal component to PureComponent
- Add docs for props of ChannelNotificationsModal
- Remove duplicated ChannelNotificationModal code from ChannelHeader and
Navbar

* Move QuickSwitchModal to ModalController

* Refactor ModalController component

- Change to PureComponent
- Add docs for props

* Change Navbar component to PureComponent

* Migrate NavBar code to be use mattermost-redux

* Refactor actions in NavBar

* Extract SetChannelHeaderOption component from NavBar

* Extract ViewChannelInfoOption component from NavBar

* Extract NotificationPreferencesOption component from NavBar

* Extract ChannelMembersOption component from NavBar

* Extract ViewPinnedPostsOption component from NavBar

* Extract AddMembersOption component from NavBar

* Extract SetChannelPurposeOption component from NavBar

* Extract RenameChannelOption component from NavBar

* Extract ConvertChannelOption component from NavBar

* Extract DeleteChannelOption component from NavBar

* Extract LeaveChannelOption component from NavBar

* Extract ToggleFavoriteChannel component from NavBar

* Fix typo in ModalIdentifiers

* Extract WebrtcOption component from NavBar

* Building clean dropdown code, start from AddMembers

* ... ViewChannelInfo

* ... ViewPinnedPosts

* ... NotificationPreferences

* ... ToggleFavoriteChannel

* ... SetChannelHeader

* ... SetChannelPurpose

* ... ViewMembers

* ... ManageMembers

* ... ConvertChannel

* ... RenameChannel

* ... DeleteChannel

* ... LeaveChannel

* ... WebRTC

* Add draft of ChannelHeaderDropdown component

* Connect MobileChannelHeaderComponent to redux store

* Change the default rendering of dropdown

* Clean Navbar component with the new dropdown component

* Remove more

* Extract ShowSearchButton component from Navbar

* Remove undefined prop

* Remove unnecessary wrapper

* Comment on collapseButtons

* Refactor Navbar even more

- Extract collapse buttons
- Remove code that is unused or never called
- Remove jQuery

* Fix character of close button

* Make channel prop to be required

* Remove unused codes

* Fix ToggleFavorite menu

* [MM-11162] Add mute icon to mobile view (#1744)

* add mute icon to mobile view

* use let or const as necessary and do some clean up

Modified.

* Cherry-picked MM-11577: Back to previous channel on archive (#1775)

* Fix the composition of navbar

TODO:
- Notification preference would not be updated when change

* Fix NotificationPreference modal

* Extract ToggleMuteChannel comp from ChannelHeader

* Cleanup ChannelHeader with new ChannelHeaderDropdown

* Update snapshot of NotificationPreferences menu

* Refactor ChannelHeader component

* Fix tests

* MM-12150 - Updating x icon and plugins (#1841)

* Fix tests

* Rename Navbar to ChannelHeaderMobile

- Rename `navbar` to `channel_header_mobile`
- Rename `navbar_info_button` to `channel_info_button`

* MM-12503: Show Mute/Unmute menu item for GMs too. (#1847)

* Remove comment from view

* Change ext of modified components to .js instead of .jsx

* MM-12494 Removing some flux store usage. (#1882)

* Removing some flux store usage.

* Feedback fixes

Cherry-picked.

* Fix some filename extensions

* Migrate ChannelActions joinChannel and leaveChannel to redux (#1898)

* Migrate ChannelActions favorite and unfavorite channel to redux

* Fix unit test after rebase

* Migrate ChannelActions joinChannel and leaveChannel to redux

Cherry-picked

* Fix a updated snaphost

* Update ModalController snapshot

* Cherry-picked: MM-12497 Remove flux store usages from user settings (#1915)

* Remove Flux store usages from account settings modal

* Fix test

* Remove global action for opening account settings modal

* Fix notifications not updating correctly

* Refactor executeCommand to remove import cycle

* Drop WebRTC from ChannelHeader

* Remove deprecated lifecycle method from ChannelHeader

* Replace hardcoded isMobile to utils

* Fix import annotations

* Migrate current channel state selectors to use mattermost-redux

* Remove unused declarations

* Change ToggleMuteChannel component to PureComponent class style to avoid arrow function in render

* Remove unnecessary arrow function in render

* Rollback mapStateToProps to plain function

* Change UnmuteChannelButton component to PureComponent class style to avoid arrow function in render

* Change file ext for UnmuteChannelButton to .js

* Move back QuickSwitchModal to ChannelHeader (Desktop only), and use redux action

* Change ShowSearchButton component to PureComponent class style to avoid arrow function in render

* Rollback mapStateToProps for ViewPinnedPosts to plain function

* Cherry-picked: Remove flux usages from email invite modal (#1954)

* Fix Channel Header

* Fix ShowSearchButton in ChannelHeaderMobile

* Fix ChannelMembersModal on PopoverListMembers

* Migrate test files to component path

* fix eslint style error on test mock

* Update per feedback

* Fix ChannelMembersModal test, IDK when it have been break exactly

* Fix errors on dropdown

* Fix view_and_manage_members to show different name by permission

* Fix to show RenameChannel on defaultChannel

* Refactor ChannelMembersModal to use redux action

* Fix ChannelNotificationModal to handle hide anim correctly

* Fix ViewAndManageMembers to be view mode only in default channel
parent 8b140211
......@@ -19,10 +19,13 @@ describe('components/ChannelHeader', () => {
closeRightHandSide: jest.fn(),
updateRhsState: jest.fn(),
openModal: jest.fn(),
closeModal: jest.fn(),
getCustomEmojisInText: jest.fn(),
updateChannelNotifyProps: jest.fn(),
goToLastViewedChannel: jest.fn(),
},
teamUrl: 'team_url',
teamId: 'team_id',
channel: {},
channelMember: {},
currentUser: {},
......
......@@ -6,7 +6,7 @@ import React from 'react';
import {FormattedMessage} from 'react-intl';
import {OverlayTrigger, Tooltip} from 'react-bootstrap';
import {Constants} from 'utils/constants.jsx';
import {Constants} from 'utils/constants';
import {t} from 'utils/i18n';
export default function HeaderIconWrapper({
......
......@@ -9,7 +9,7 @@ import MentionsIcon from 'components/svg/mentions_icon';
import PinIcon from 'components/svg/pin_icon';
import SearchIcon from 'components/svg/search_icon';
import HeaderIconWrapper from 'components/channel_header/components/header_icon_wrapper.jsx';
import HeaderIconWrapper from 'components/channel_header/components/header_icon_wrapper';
describe('components/channel_header/components/HeaderIconWrapper', () => {
function emptyFunction() {} //eslint-disable-line no-empty-function
......
// 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 {favoriteChannel, leaveChannel, unfavoriteChannel, updateChannelNotifyProps} from 'mattermost-redux/actions/channels';
import {connect} from 'react-redux';
import {withRouter} from 'react-router-dom';
import {
favoriteChannel,
unfavoriteChannel,
updateChannelNotifyProps,
} from 'mattermost-redux/actions/channels';
import {getCustomEmojisInText} from 'mattermost-redux/actions/emojis';
import {General} from 'mattermost-redux/constants';
import {getChannel, getMyChannelMember, isCurrentChannelReadOnly} from 'mattermost-redux/selectors/entities/channels';
import {getMyTeamMember} from 'mattermost-redux/selectors/entities/teams';
import {getCurrentUser, getUser} from 'mattermost-redux/selectors/entities/users';
import {getUserIdFromChannelName, isDefault, isFavoriteChannel} from 'mattermost-redux/utils/channel_utils';
import {getLicense} from 'mattermost-redux/selectors/entities/general';
import {withRouter} from 'react-router-dom';
import {
getCurrentChannel,
getMyCurrentChannelMembership,
isCurrentChannelFavorite,
isCurrentChannelMuted,
isCurrentChannelReadOnly,
} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {
getCurrentUser,
getUser,
} from 'mattermost-redux/selectors/entities/users';
import {getUserIdFromChannelName} from 'mattermost-redux/utils/channel_utils';
import {goToLastViewedChannel} from 'actions/views/channel';
import {getPenultimateViewedChannelName} from 'selectors/local_storage';
import {Constants} from 'utils/constants.jsx';
import {openModal, closeModal} from 'actions/views/modals';
import {
showFlaggedPosts,
showPinnedPosts,
......@@ -26,14 +34,12 @@ import {
closeRightHandSide,
updateRhsState,
} from 'actions/views/rhs';
import {openModal} from 'actions/views/modals';
import {getRhsState} from 'selectors/rhs';
import ChannelHeader from './channel_header.jsx';
import ChannelHeader from './channel_header';
function mapStateToProps(state, ownProps) {
const channel = getChannel(state, ownProps.channelId) || {};
const prefs = state.entities.preferences.myPreferences;
const mapStateToProps = (state) => {
const channel = getCurrentChannel(state) || {};
const user = getCurrentUser(state);
let dmUser;
......@@ -42,45 +48,34 @@ function mapStateToProps(state, ownProps) {
dmUser = getUser(state, dmUserId);
}
const license = getLicense(state);
let penultimateViewedChannelName = getPenultimateViewedChannelName(state);
if (!penultimateViewedChannelName) {
penultimateViewedChannelName = Constants.DEFAULT_CHANNEL;
}
return {
teamId: getCurrentTeamId(state),
channel,
channelMember: getMyChannelMember(state, ownProps.channelId),
teamMember: getMyTeamMember(state, channel.team_id),
isFavorite: isFavoriteChannel(prefs, ownProps.channelId),
isDefault: isDefault(channel),
channelMember: getMyCurrentChannelMembership(state),
currentUser: user,
dmUser,
rhsState: getRhsState(state),
isLicensed: license.IsLicensed === 'true',
isFavorite: isCurrentChannelFavorite(state),
isReadOnly: isCurrentChannelReadOnly(state),
penultimateViewedChannelName,
isMuted: isCurrentChannelMuted(state),
};
}
};
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
leaveChannel,
favoriteChannel,
unfavoriteChannel,
showFlaggedPosts,
showPinnedPosts,
showMentions,
closeRightHandSide,
updateRhsState,
openModal,
getCustomEmojisInText,
updateChannelNotifyProps,
goToLastViewedChannel,
}, dispatch),
};
}
const mapDispatchToProps = (dispatch) => ({
actions: bindActionCreators({
favoriteChannel,
unfavoriteChannel,
showFlaggedPosts,
showPinnedPosts,
showMentions,
closeRightHandSide,
updateRhsState,
getCustomEmojisInText,
updateChannelNotifyProps,
goToLastViewedChannel,
openModal,
closeModal,
}, dispatch),
});
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(ChannelHeader));
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import PropTypes from 'prop-types';
import * as MenuItem from './menu_items';
export default class ChannelHeaderDropdown extends React.PureComponent {
static propTypes = {
user: PropTypes.object.isRequired,
channel: PropTypes.object.isRequired,
isDefault: PropTypes.bool.isRequired,
isReadonly: PropTypes.bool.isRequired,
isMuted: PropTypes.bool.isRequired,
isArchived: PropTypes.bool.isRequired,
}
render() {
const {
user,
channel,
isDefault,
isMuted,
isReadonly,
isArchived,
} = this.props;
return (
<ul
id='channelHeaderDropdownMenu'
className='dropdown-menu'
role='menu'
aria-labelledby='channel_header_dropdown'
>
<MenuItem.Group>
<MenuItem.ViewChannelInfo channel={channel}/>
<MenuItem.NotificationPreferences
user={user}
channel={channel}
isArchived={isArchived}
/>
<MenuItem.ToggleMuteChannel
user={user}
channel={channel}
isMuted={isMuted}
isArchived={isArchived}
/>
</MenuItem.Group>
<MenuItem.Group>
<MenuItem.AddMembers
channel={channel}
isDefault={isDefault}
isArchived={isArchived}
/>
<MenuItem.ViewAndManageMembers
channel={channel}
isDefault={isDefault}
/>
</MenuItem.Group>
<MenuItem.Group>
<MenuItem.SetChannelHeader
channel={channel}
isArchived={isArchived}
isReadonly={isReadonly}
/>
<MenuItem.SetChannelPurpose
channel={channel}
isArchived={isArchived}
isReadonly={isReadonly}
/>
<MenuItem.RenameChannel
channel={channel}
isArchived={isArchived}
/>
<MenuItem.ConvertChannel
channel={channel}
isDefault={isDefault}
isArchived={isArchived}
/>
<MenuItem.DeleteChannel
channel={channel}
isDefault={isDefault}
isArchived={isArchived}
/>
</MenuItem.Group>
<MenuItem.Group showDivider={false}>
<MenuItem.LeaveChannel
channel={channel}
isDefault={isDefault}
/>
<MenuItem.CloseChannel isArchived={isArchived}/>
</MenuItem.Group>
</ul>
);
}
}
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {connect} from 'react-redux';
import {createSelector} from 'reselect';
import {
getCurrentUser,
getUserStatuses,
} from 'mattermost-redux/selectors/entities/users';
import {
getCurrentChannel,
isCurrentChannelDefault,
isCurrentChannelFavorite,
isCurrentChannelMuted,
isCurrentChannelArchived,
isCurrentChannelReadOnly,
} from 'mattermost-redux/selectors/entities/channels';
import {Constants} from 'utils/constants';
import * as Utils from 'utils/utils';
import Desktop from './channel_header_dropdown';
import Mobile from './mobile_channel_header_dropdown';
const getTeammateId = createSelector(
getCurrentChannel,
(channel) => {
if (channel.type !== Constants.DM_CHANNEL) {
return null;
}
return Utils.getUserIdFromChannelName(channel);
},
);
const getTeammateStatus = createSelector(
getUserStatuses,
getTeammateId,
(userStatuses, teammateId) => {
if (!teammateId) {
return null;
}
return userStatuses[teammateId];
}
);
const mapStateToProps = (state) => ({
user: getCurrentUser(state),
channel: getCurrentChannel(state),
isDefault: isCurrentChannelDefault(state),
isFavorite: isCurrentChannelFavorite(state),
isMuted: isCurrentChannelMuted(state),
isReadonly: isCurrentChannelReadOnly(state),
isArchived: isCurrentChannelArchived(state),
teammateId: getTeammateId(state),
teammateStatus: getTeammateStatus(state),
});
export const ChannelHeaderDropdown = connect(mapStateToProps)(Desktop);
export const MobileChannelHeaderDropdown = connect(mapStateToProps)(Mobile);
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ChannelHeaderDropdown/MenuItem.AddMembers should match snapshot 1`] = `
<Connect(ChannelPermissionGate)
channelId="channel_id"
permissions={
Array [
"manage_public_channel_members",
]
}
teamId="team_id"
>
<li
role="presentation"
>
<Connect(ModalToggleButtonRedux)
dialogProps={
Object {
"channel": Object {
"id": "channel_id",
"team_id": "team_id",
"type": "O",
},
}
}
dialogType={[Function]}
modalId="channel_invite"
role="menuitem"
>
<FormattedMessage
defaultMessage="Add Members"
id="navbar.addMembers"
values={Object {}}
/>
</Connect(ModalToggleButtonRedux)>
</li>
</Connect(ChannelPermissionGate)>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ChannelHeaderDropdown/MenuItem.ConvertChannel should match snapshot 1`] = `
<Connect(TeamPermissionGate)
permissions={
Array [
"manage_team",
]
}
teamId="team_id"
>
<li
role="presentation"
>
<Connect(ModalToggleButtonRedux)
dialogProps={
Object {
"channelDisplayName": "Test Channel",
"channelId": "channel_id",
}
}
dialogType={[Function]}
modalId="convert_channel"
role="menuitem"
>
<FormattedMessage
defaultMessage="Convert to Private Channel"
id="channel_header.convert"
values={Object {}}
/>
</Connect(ModalToggleButtonRedux)>
</li>
</Connect(TeamPermissionGate)>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ChannelHeaderDropdown/MenuItem.Group should match snapshot 1`] = `
<Fragment>
children
<li
className="divider"
/>
</Fragment>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ChannelHeaderDropdown/MenuItem.NotificationPreferences should match snapshot 1`] = `
<li
role="presentation"
>
<Connect(ModalToggleButtonRedux)
dialogProps={
Object {
"channel": Object {
"type": "O",
},
"currentUser": Object {},
}
}
dialogType={[Function]}
modalId="channel_notifications"
role="menuitem"
>
<FormattedMessage
defaultMessage="Notification Preferences"
id="navbar.preferences"
values={Object {}}
/>
</Connect(ModalToggleButtonRedux)>
</li>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ChannelHeaderDropdown/MenuItem.RenameChannel should match snapshot 1`] = `
<Connect(ChannelPermissionGate)
channelId="channel_id"
permissions={
Array [
"manage_public_channel_properties",
]
}
teamId="team_id"
>
<li
role="presentation"
>
<Connect(ModalToggleButtonRedux)
dialogProps={
Object {
"channel": Object {
"id": "channel_id",
"team_id": "team_id",
"type": "O",
},
}
}
dialogType={[Function]}
modalId="rename_channel"
role="menuitem"
>
<FormattedMessage
defaultMessage="Rename Channel"
id="channel_header.rename"
values={Object {}}
/>
</Connect(ModalToggleButtonRedux)>
</li>
</Connect(ChannelPermissionGate)>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ChannelHeaderDropdown/MenuItem.SetChannelHeader should match snapshot 1`] = `
<Connect(ChannelPermissionGate)
channelId="channel_id"
permissions={
Array [
"manage_public_channel_properties",
]
}
teamId="team_id"
>
<li
role="presentation"
>
<Connect(ModalToggleButtonRedux)
dialogProps={
Object {
"channel": Object {
"id": "channel_id",
"team_id": "team_id",
"type": "O",
},
}
}
dialogType={[Function]}
id="editChannelHeader"
modalId="edit_channel_header"
role="menuitem"
>
<FormattedMessage
defaultMessage="Edit Channel Header"
id="channel_header.setHeader"
values={Object {}}
/>
</Connect(ModalToggleButtonRedux)>
</li>
</Connect(ChannelPermissionGate)>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ChannelHeaderDropdown/MenuItem.SetChannelPurpose should match snapshot 1`] = `
<Connect(ChannelPermissionGate)
channelId="channel_id"
permissions={
Array [
"manage_public_channel_properties",
]
}
teamId="team_id"
>
<li
role="presentation"
>
<Connect(ModalToggleButtonRedux)
dialogProps={
Object {
"channel": Object {
"id": "channel_id",
"team_id": "team_id",
"type": "O",
},
}
}
dialogType={[Function]}
modalId="edit_channel_purpose"
role="menuitem"
>
<FormattedMessage
defaultMessage="Edit Channel Purpose"
id="channel_header.setPurpose"
values={Object {}}
/>
</Connect(ModalToggleButtonRedux)>
</li>
</Connect(ChannelPermissionGate)>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ChannelHeaderDropdown/MenuItem.ViewAndManageMembers should match snapshot 1`] = `
<li
role="presentation"
>
<Connect(ModalToggleButtonRedux)
dialogProps={
Object {
"channel": Object {
"type": "O",
},
}
}
dialogType={[Function]}
modalId="channel_members"
role="menuitem"
>
<Connect(ChannelPermissionGate)
permissions={