MM42267: Add member count in the browse channel modal (#23800)
* add base for calling the endpoint * add endpoint and handler * update store and layers * call the endpoint * align types * update app layers * generate mocks * complete handler * finish store query * add todos * add ui for member count * add selector * add a todo * add cache layer * optimize calls in FE * handle invalidation of the cache * fix go style * fix test * use existing channel layer count * fix import error * delete unnecessary code * write tests for channel cache layer * fix testname * fix mocks * fix cache layer test * fix a test * really fix the test * write more tests for server * address PR comments * remove comment * rename more_channels to browse_channels * fix style * update snapshot * add translations * Revert "add translations" This reverts commit 56476a5dabe357703ef02be9a38b3e88f5c8b1e7. * add only related translations * address PR review points * add test * fix test --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
4803889158
Коммит
628273d98d
@@ -1,8 +1,8 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/MoreChannels should match snapshot and state 1`] = `
|
||||
exports[`components/BrowseChannels should match snapshot and state 1`] = `
|
||||
<GenericModal
|
||||
aria-labelledby="moreChannelsModalLabel"
|
||||
aria-labelledby="browseChannelsModalLabel"
|
||||
aria-modal={true}
|
||||
autoCloseOnCancelButton={true}
|
||||
autoCloseOnConfirmButton={false}
|
||||
@@ -32,7 +32,7 @@ exports[`components/MoreChannels should match snapshot and state 1`] = `
|
||||
</button>
|
||||
</Memo(Connect(TeamPermissionGate))>
|
||||
}
|
||||
id="moreChannelsModal"
|
||||
id="browseChannelsModal"
|
||||
keyboardEscape={true}
|
||||
modalHeaderText={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
@@ -1,6 +1,6 @@
|
||||
@charset 'UTF-8';
|
||||
|
||||
#moreChannelsModal {
|
||||
#browseChannelsModal {
|
||||
.modal-content {
|
||||
min-height: 600px;
|
||||
max-height: calc(50vh - 240px);
|
||||
@@ -5,8 +5,9 @@ import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import {ActionResult} from 'mattermost-redux/types/actions';
|
||||
import {Channel} from '@mattermost/types/channels';
|
||||
|
||||
import MoreChannels, {Props} from 'components/more_channels/more_channels';
|
||||
import BrowseChannels, {Props} from 'components/browse_channels/browse_channels';
|
||||
import SearchableChannelList from 'components/searchable_channel_list';
|
||||
|
||||
import {getHistory} from 'utils/browser_history';
|
||||
@@ -14,7 +15,7 @@ import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
jest.useFakeTimers('legacy');
|
||||
|
||||
describe('components/MoreChannels', () => {
|
||||
describe('components/BrowseChannels', () => {
|
||||
const searchResults = {
|
||||
data: [{
|
||||
id: 'channel-id-1',
|
||||
@@ -29,6 +30,15 @@ describe('components/MoreChannels', () => {
|
||||
}],
|
||||
};
|
||||
|
||||
const archivedChannel = TestHelper.getChannelMock({
|
||||
id: 'channel_id_2',
|
||||
team_id: 'channel_team_2',
|
||||
display_name: 'channel-2',
|
||||
name: 'channel-2',
|
||||
header: 'channel-2-header',
|
||||
purpose: 'channel-2-purpose',
|
||||
});
|
||||
|
||||
const channelActions = {
|
||||
joinChannelAction: (userId: string, teamId: string, channelId: string): Promise<ActionResult> => {
|
||||
return new Promise((resolve) => {
|
||||
@@ -56,18 +66,25 @@ describe('components/MoreChannels', () => {
|
||||
return resolve(searchResults);
|
||||
});
|
||||
},
|
||||
getChannels: (): Promise<ActionResult<Channel[], Error>> => {
|
||||
return new Promise((resolve) => {
|
||||
return resolve({
|
||||
data: [TestHelper.getChannelMock({})],
|
||||
});
|
||||
});
|
||||
},
|
||||
getArchivedChannels: (): Promise<ActionResult<Channel[], Error>> => {
|
||||
return new Promise((resolve) => {
|
||||
return resolve({
|
||||
data: [archivedChannel],
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const baseProps: Props = {
|
||||
channels: [TestHelper.getChannelMock({})],
|
||||
archivedChannels: [TestHelper.getChannelMock({
|
||||
id: 'channel_id_2',
|
||||
team_id: 'channel_team_2',
|
||||
display_name: 'channel-2',
|
||||
name: 'channel-2',
|
||||
header: 'channel-2-header',
|
||||
purpose: 'channel-2-purpose',
|
||||
})],
|
||||
archivedChannels: [archivedChannel],
|
||||
currentUserId: 'user-1',
|
||||
teamId: 'team_id',
|
||||
teamName: 'team_name',
|
||||
@@ -76,20 +93,21 @@ describe('components/MoreChannels', () => {
|
||||
shouldHideJoinedChannels: false,
|
||||
myChannelMemberships: {},
|
||||
actions: {
|
||||
getChannels: jest.fn(),
|
||||
getArchivedChannels: jest.fn(),
|
||||
getChannels: jest.fn(channelActions.getChannels),
|
||||
getArchivedChannels: jest.fn(channelActions.getArchivedChannels),
|
||||
joinChannel: jest.fn(channelActions.joinChannelAction),
|
||||
searchMoreChannels: jest.fn(channelActions.searchMoreChannels),
|
||||
openModal: jest.fn(),
|
||||
closeModal: jest.fn(),
|
||||
closeRightHandSide: jest.fn(),
|
||||
setGlobalItem: jest.fn(),
|
||||
getChannelsMemberCount: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
test('should match snapshot and state', () => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
@@ -105,8 +123,8 @@ describe('components/MoreChannels', () => {
|
||||
});
|
||||
|
||||
test('should call closeModal on handleExit', () => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...baseProps}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().handleExit();
|
||||
@@ -114,8 +132,8 @@ describe('components/MoreChannels', () => {
|
||||
});
|
||||
|
||||
test('should match state on onChange', () => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...baseProps}/>,
|
||||
);
|
||||
wrapper.setState({searchedChannels: [TestHelper.getChannelMock({id: 'other_channel_id'})]});
|
||||
|
||||
@@ -129,8 +147,8 @@ describe('components/MoreChannels', () => {
|
||||
});
|
||||
|
||||
test('should call props.getChannels on nextPage', () => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...baseProps}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().nextPage(1);
|
||||
@@ -141,7 +159,7 @@ describe('components/MoreChannels', () => {
|
||||
|
||||
test('should have loading prop true when searching state is true', () => {
|
||||
const wrapper = shallow(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
<BrowseChannels {...baseProps}/>,
|
||||
);
|
||||
|
||||
wrapper.setState({search: true, searching: true});
|
||||
@@ -164,8 +182,8 @@ describe('components/MoreChannels', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...props}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...props}/>,
|
||||
);
|
||||
|
||||
const callback = jest.fn();
|
||||
@@ -192,8 +210,8 @@ describe('components/MoreChannels', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...props}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...props}/>,
|
||||
);
|
||||
|
||||
const callback = jest.fn();
|
||||
@@ -208,8 +226,8 @@ describe('components/MoreChannels', () => {
|
||||
});
|
||||
|
||||
test('should not perform a search if term is empty', () => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...baseProps}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().onChange = jest.fn();
|
||||
@@ -223,8 +241,8 @@ describe('components/MoreChannels', () => {
|
||||
});
|
||||
|
||||
test('should handle a failed search', (done) => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...baseProps}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().onChange = jest.fn();
|
||||
@@ -251,8 +269,8 @@ describe('components/MoreChannels', () => {
|
||||
});
|
||||
|
||||
test('should perform search and set the correct state', (done) => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...baseProps}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().onChange = jest.fn();
|
||||
@@ -277,8 +295,8 @@ describe('components/MoreChannels', () => {
|
||||
});
|
||||
|
||||
test('should perform search on archived channels and set the correct state', (done) => {
|
||||
const wrapper = shallow<MoreChannels>(
|
||||
<MoreChannels {...baseProps}/>,
|
||||
const wrapper = shallow<BrowseChannels>(
|
||||
<BrowseChannels {...baseProps}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().onChange = jest.fn();
|
||||
@@ -24,15 +24,15 @@ import classNames from 'classnames';
|
||||
import {localizeMessage} from 'utils/utils';
|
||||
import LoadingScreen from 'components/loading_screen';
|
||||
|
||||
import './more_channels.scss';
|
||||
import './browse_channels.scss';
|
||||
|
||||
const CHANNELS_CHUNK_SIZE = 50;
|
||||
const CHANNELS_PER_PAGE = 50;
|
||||
const SEARCH_TIMEOUT_MILLISECONDS = 100;
|
||||
|
||||
type Actions = {
|
||||
getChannels: (teamId: string, page: number, perPage: number) => void;
|
||||
getArchivedChannels: (teamId: string, page: number, channelsPerPage: number) => void;
|
||||
getChannels: (teamId: string, page: number, perPage: number) => Promise<ActionResult<Channel[], Error>>;
|
||||
getArchivedChannels: (teamId: string, page: number, channelsPerPage: number) => Promise<ActionResult<Channel[], Error>>;
|
||||
joinChannel: (currentUserId: string, teamId: string, channelId: string) => Promise<ActionResult>;
|
||||
searchMoreChannels: (term: string, shouldShowArchivedChannels: boolean, shouldHideJoinedChannels: boolean) => Promise<ActionResult>;
|
||||
openModal: <P>(modalData: ModalData<P>) => void;
|
||||
@@ -43,6 +43,7 @@ type Actions = {
|
||||
*/
|
||||
setGlobalItem: (name: string, value: string) => void;
|
||||
closeRightHandSide: () => void;
|
||||
getChannelsMemberCount: (channelIds: string[]) => Promise<ActionResult>;
|
||||
}
|
||||
|
||||
export type Props = {
|
||||
@@ -58,6 +59,7 @@ export type Props = {
|
||||
shouldHideJoinedChannels: boolean;
|
||||
rhsState?: RhsState;
|
||||
rhsOpen?: boolean;
|
||||
channelsMemberCount?: Record<string, number>;
|
||||
actions: Actions;
|
||||
}
|
||||
|
||||
@@ -71,7 +73,7 @@ type State = {
|
||||
searchTerm: string;
|
||||
}
|
||||
|
||||
export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
export default class BrowseChannels extends React.PureComponent<Props, State> {
|
||||
public searchTimeoutId: number;
|
||||
activeChannels: Channel[] = [];
|
||||
|
||||
@@ -92,10 +94,23 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.actions.getChannels(this.props.teamId, 0, CHANNELS_CHUNK_SIZE * 2);
|
||||
const promises = [
|
||||
this.props.actions.getChannels(this.props.teamId, 0, CHANNELS_CHUNK_SIZE * 2),
|
||||
];
|
||||
|
||||
if (this.props.canShowArchivedChannels) {
|
||||
this.props.actions.getArchivedChannels(this.props.teamId, 0, CHANNELS_CHUNK_SIZE * 2);
|
||||
promises.push(this.props.actions.getArchivedChannels(this.props.teamId, 0, CHANNELS_CHUNK_SIZE * 2));
|
||||
}
|
||||
|
||||
Promise.all(promises).then((results) => {
|
||||
const channelIDsForMemberCount = results.flatMap((result) => {
|
||||
return result.data ? result.data.map((channel) => channel.id) : [];
|
||||
},
|
||||
);
|
||||
if (channelIDsForMemberCount.length > 0) {
|
||||
this.props.actions.getChannelsMemberCount(channelIDsForMemberCount);
|
||||
}
|
||||
});
|
||||
this.loadComplete();
|
||||
}
|
||||
|
||||
@@ -134,7 +149,11 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
nextPage = (page: number) => {
|
||||
this.props.actions.getChannels(this.props.teamId, page + 1, CHANNELS_PER_PAGE);
|
||||
this.props.actions.getChannels(this.props.teamId, page + 1, CHANNELS_PER_PAGE).then((result) => {
|
||||
if (result.data && result.data.length > 0) {
|
||||
this.props.actions.getChannelsMemberCount(result.data.map((channel) => channel.id));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleJoin = async (channel: Channel, done: () => void) => {
|
||||
@@ -148,6 +167,7 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
if (result?.error) {
|
||||
this.setState({serverError: result.error.message});
|
||||
} else {
|
||||
this.props.actions.getChannelsMemberCount([channel.id]);
|
||||
getHistory().push(getRelativeChannelURL(teamName, channel.name));
|
||||
this.closeEditRHS();
|
||||
}
|
||||
@@ -211,9 +231,6 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
this.props.actions.setGlobalItem(StoragePrefixes.HIDE_JOINED_CHANNELS, shouldHideJoinedChannels.toString());
|
||||
};
|
||||
|
||||
otherChannelsWithoutJoined = this.props.channels.filter((channel) => !this.isMemberOfChannel(channel.id));
|
||||
archivedChannelsWithoutJoined = this.props.archivedChannels.filter((channel) => !this.isMemberOfChannel(channel.id));
|
||||
|
||||
render() {
|
||||
const {
|
||||
channels,
|
||||
@@ -304,6 +321,7 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
closeModal={this.props.actions.closeModal}
|
||||
hideJoinedChannelsPreference={this.handleShowJoinedChannelsPreference}
|
||||
rememberHideJoinedChannelsChecked={shouldHideJoinedChannels}
|
||||
channelsMemberCount={this.props.channelsMemberCount}
|
||||
/>
|
||||
{serverError}
|
||||
</React.Fragment>
|
||||
@@ -319,8 +337,8 @@ export default class MoreChannels extends React.PureComponent<Props, State> {
|
||||
return (
|
||||
<GenericModal
|
||||
onExited={this.handleExit}
|
||||
id='moreChannelsModal'
|
||||
aria-labelledby='moreChannelsModalLabel'
|
||||
id='browseChannelsModal'
|
||||
aria-labelledby='browseChannelsModalLabel'
|
||||
compassDesign={true}
|
||||
modalHeaderText={title}
|
||||
headerButton={createNewChannelButton('outlineButton')}
|
||||
@@ -11,8 +11,8 @@ import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
import {Action, ActionResult} from 'mattermost-redux/types/actions';
|
||||
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getChannels, getArchivedChannels, joinChannel} from 'mattermost-redux/actions/channels';
|
||||
import {getChannelsInCurrentTeam, getMyChannelMemberships} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getChannels, getArchivedChannels, joinChannel, getChannelsMemberCount} from 'mattermost-redux/actions/channels';
|
||||
import {getChannelsInCurrentTeam, getMyChannelMemberships, getChannelsMemberCount as getChannelsMemberCountSelector} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import {searchMoreChannels} from 'actions/channel_actions';
|
||||
import {openModal, closeModal} from 'actions/views/modals';
|
||||
@@ -23,7 +23,7 @@ import {getIsRhsOpen, getRhsState} from 'selectors/rhs';
|
||||
import {ModalData} from 'types/actions';
|
||||
import {GlobalState} from 'types/store';
|
||||
|
||||
import MoreChannels from './more_channels';
|
||||
import BrowseChannels from './browse_channels';
|
||||
import {makeGetGlobalItem} from 'selectors/storage';
|
||||
import Constants, {StoragePrefixes} from 'utils/constants';
|
||||
import {setGlobalItem} from 'actions/storage';
|
||||
@@ -56,18 +56,20 @@ function mapStateToProps(state: GlobalState) {
|
||||
shouldHideJoinedChannels: getGlobalItem(state) === 'true',
|
||||
rhsState: getRhsState(state),
|
||||
rhsOpen: getIsRhsOpen(state),
|
||||
channelsMemberCount: getChannelsMemberCountSelector(state),
|
||||
};
|
||||
}
|
||||
|
||||
type Actions = {
|
||||
getChannels: (teamId: string, page: number, perPage: number) => void;
|
||||
getArchivedChannels: (teamId: string, page: number, channelsPerPage: number) => void;
|
||||
getChannels: (teamId: string, page: number, perPage: number) => Promise<ActionResult<Channel[], Error>>;
|
||||
getArchivedChannels: (teamId: string, page: number, channelsPerPage: number) => Promise<ActionResult<Channel[], Error>>;
|
||||
joinChannel: (currentUserId: string, teamId: string, channelId: string) => Promise<ActionResult>;
|
||||
searchMoreChannels: (term: string, shouldShowArchivedChannels: boolean) => Promise<ActionResult>;
|
||||
openModal: <P>(modalData: ModalData<P>) => void;
|
||||
closeModal: (modalId: string) => void;
|
||||
setGlobalItem: (name: string, value: string) => void;
|
||||
closeRightHandSide: () => void;
|
||||
getChannelsMemberCount: (channelIds: string[]) => Promise<ActionResult>;
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch) {
|
||||
@@ -81,8 +83,9 @@ function mapDispatchToProps(dispatch: Dispatch) {
|
||||
closeModal,
|
||||
setGlobalItem,
|
||||
closeRightHandSide,
|
||||
getChannelsMemberCount,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MoreChannels);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(BrowseChannels);
|
||||
@@ -4,7 +4,7 @@
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {ArchiveOutlineIcon, CheckIcon, ChevronDownIcon, GlobeIcon, LockOutlineIcon, MagnifyIcon} from '@mattermost/compass-icons/components';
|
||||
import {ArchiveOutlineIcon, CheckIcon, ChevronDownIcon, GlobeIcon, LockOutlineIcon, MagnifyIcon, AccountOutlineIcon} from '@mattermost/compass-icons/components';
|
||||
import {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
import {RelationOneToOne} from '@mattermost/types/utilities';
|
||||
import {isPrivateChannel} from 'mattermost-redux/utils/channel_utils';
|
||||
@@ -46,6 +46,7 @@ type Props = {
|
||||
rememberHideJoinedChannelsChecked: boolean;
|
||||
canShowArchivedChannels?: boolean;
|
||||
loading?: boolean;
|
||||
channelsMemberCount?: Record<string, number>;
|
||||
}
|
||||
|
||||
type State = {
|
||||
@@ -133,6 +134,10 @@ export default class SearchableChannelList extends React.PureComponent<Props, St
|
||||
} else {
|
||||
channelTypeIcon = <GlobeIcon size={18}/>;
|
||||
}
|
||||
let memberCount = 0;
|
||||
if (this.props.channelsMemberCount?.[channel.id]) {
|
||||
memberCount = this.props.channelsMemberCount[channel.id];
|
||||
}
|
||||
|
||||
const membershipIndicator = this.isMemberOfChannel(channel.id) ? (
|
||||
<div
|
||||
@@ -149,8 +154,8 @@ export default class SearchableChannelList extends React.PureComponent<Props, St
|
||||
|
||||
const channelPurposeContainerAriaLabel = localizeAndFormatMessage(
|
||||
t('more_channels.channel_purpose'),
|
||||
'Channel Information: Membership Indicator: Joined, Purpose: {channelPurpose}',
|
||||
{channelPurpose: channel.purpose || ''},
|
||||
'Channel Information: Membership Indicator: Joined, Member count {memberCount}, Purpose: {channelPurpose}',
|
||||
{memberCount, channelPurpose: channel.purpose || ''},
|
||||
);
|
||||
|
||||
const channelPurposeContainer = (
|
||||
@@ -159,7 +164,10 @@ export default class SearchableChannelList extends React.PureComponent<Props, St
|
||||
aria-label={channelPurposeContainerAriaLabel}
|
||||
>
|
||||
{membershipIndicator}
|
||||
{(channel.purpose.length > 0 && membershipIndicator) ? <span className='dot'/> : null}
|
||||
{membershipIndicator ? <span className='dot'/> : null}
|
||||
<AccountOutlineIcon size={14}/>
|
||||
<span data-testid={`channelMemberCount-${channel.name}`} >{memberCount}</span>
|
||||
{channel.purpose.length > 0 ? <span className='dot'/> : null}
|
||||
<span className='more-modal__description'>{channel.purpose}</span>
|
||||
</div>
|
||||
);
|
||||
@@ -195,6 +203,7 @@ export default class SearchableChannelList extends React.PureComponent<Props, St
|
||||
className='more-modal__row'
|
||||
key={channel.id}
|
||||
id={`ChannelRow-${channel.name}`}
|
||||
data-testid={`ChannelRow-${channel.name}`}
|
||||
aria-label={ariaLabel}
|
||||
onClick={(e) => this.handleJoin(channel, e)}
|
||||
tabIndex={0}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {useSelector, useDispatch} from 'react-redux';
|
||||
|
||||
import MenuWrapper from 'components/widgets/menu/menu_wrapper';
|
||||
import Menu from 'components/widgets/menu/menu';
|
||||
import MoreChannels from 'components/more_channels';
|
||||
import BrowseChannels from 'components/browse_channels';
|
||||
import NewChannelModal from 'components/new_channel_modal/new_channel_modal';
|
||||
|
||||
import {isAddChannelCtaDropdownOpen} from 'selectors/views/add_channel_dropdown';
|
||||
@@ -59,7 +59,7 @@ const AddChannelsCtaButton = (): JSX.Element | null => {
|
||||
const showMoreChannelsModal = () => {
|
||||
dispatch(openModal({
|
||||
modalId: ModalIdentifiers.MORE_CHANNELS,
|
||||
dialogType: MoreChannels,
|
||||
dialogType: BrowseChannels,
|
||||
dialogProps: {morePublicChannelsModalType: 'public'},
|
||||
}));
|
||||
trackEvent('ui', 'browse_channels_button_is_clicked');
|
||||
|
||||
@@ -8,7 +8,7 @@ import {trackEvent} from 'actions/telemetry_actions';
|
||||
import EditCategoryModal from 'components/edit_category_modal';
|
||||
import MoreDirectChannels from 'components/more_direct_channels';
|
||||
import DataPrefetch from 'components/data_prefetch';
|
||||
import MoreChannels from 'components/more_channels';
|
||||
import BrowseChannels from 'components/browse_channels';
|
||||
import NewChannelModal from 'components/new_channel_modal/new_channel_modal';
|
||||
import InvitationModal from 'components/invitation_modal';
|
||||
import UserSettingsModal from 'components/user_settings/modal';
|
||||
@@ -155,7 +155,7 @@ export default class Sidebar extends React.PureComponent<Props, State> {
|
||||
showMoreChannelsModal = () => {
|
||||
this.props.actions.openModal({
|
||||
modalId: ModalIdentifiers.MORE_CHANNELS,
|
||||
dialogType: MoreChannels,
|
||||
dialogType: BrowseChannels,
|
||||
dialogProps: {morePublicChannelsModalType: 'public'},
|
||||
});
|
||||
trackEvent('ui', 'ui_channels_more_public_v2');
|
||||
|
||||
@@ -56,6 +56,7 @@ export default keyMirror({
|
||||
RECEIVED_CHANNEL_MEMBERS: null,
|
||||
RECEIVED_CHANNEL_MEMBER: null,
|
||||
RECEIVED_CHANNEL_STATS: null,
|
||||
RECEIVED_CHANNELS_MEMBER_COUNT: null,
|
||||
RECEIVED_CHANNEL_PROPS: null,
|
||||
RECEIVED_CHANNEL_DELETED: null,
|
||||
RECEIVED_CHANNEL_UNARCHIVED: null,
|
||||
|
||||
@@ -1083,6 +1083,27 @@ export function getChannelStats(channelId: string, excludeFilesCount?: boolean):
|
||||
};
|
||||
}
|
||||
|
||||
export function getChannelsMemberCount(channelIds: string[]): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
let channelsMemberCount;
|
||||
|
||||
try {
|
||||
channelsMemberCount = await Client4.getChannelsMemberCount(channelIds);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(error, dispatch, getState);
|
||||
dispatch(logError(error));
|
||||
return {error};
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: ChannelTypes.RECEIVED_CHANNELS_MEMBER_COUNT,
|
||||
data: channelsMemberCount,
|
||||
});
|
||||
|
||||
return {data: channelsMemberCount};
|
||||
};
|
||||
}
|
||||
|
||||
export function addChannelMember(channelId: string, userId: string, postRootId = ''): ActionFunc {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
let member;
|
||||
|
||||
@@ -713,6 +713,22 @@ function stats(state: RelationOneToOne<Channel, ChannelStats> = {}, action: Gene
|
||||
}
|
||||
}
|
||||
|
||||
function channelsMemberCount(state: Record<string, number> = {}, action: GenericAction) {
|
||||
switch (action.type) {
|
||||
case ChannelTypes.RECEIVED_CHANNELS_MEMBER_COUNT: {
|
||||
const memberCount = action.data;
|
||||
return {
|
||||
...state,
|
||||
...memberCount,
|
||||
};
|
||||
}
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
return {};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function groupsAssociatedToChannel(state: any = {}, action: GenericAction) {
|
||||
switch (action.type) {
|
||||
case GroupTypes.RECEIVED_ALL_GROUPS_ASSOCIATED_TO_CHANNELS_IN_TEAM: {
|
||||
@@ -989,4 +1005,7 @@ export default combineReducers({
|
||||
|
||||
// object where every key is the channel id mapping to an object containing the number of messages in the channel
|
||||
messageCounts,
|
||||
|
||||
// object where key is the channel id and value is the member count for the channel
|
||||
channelsMemberCount,
|
||||
});
|
||||
|
||||
@@ -98,6 +98,10 @@ export function getAllChannelStats(state: GlobalState): RelationOneToOne<Channel
|
||||
return state.entities.channels.stats;
|
||||
}
|
||||
|
||||
export function getChannelsMemberCount(state: GlobalState): Record<string, number> {
|
||||
return state.entities.channels.channelsMemberCount;
|
||||
}
|
||||
|
||||
export function getChannelsInTeam(state: GlobalState): RelationOneToMany<Team, Channel> {
|
||||
return state.entities.channels.channelsInTeam;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ const state: GlobalState = {
|
||||
channelModerations: {},
|
||||
channelMemberCountsByGroup: {},
|
||||
messageCounts: {},
|
||||
channelsMemberCount: {},
|
||||
},
|
||||
posts: {
|
||||
expandedURLs: {},
|
||||
|
||||
Ссылка в новой задаче
Block a user