diff --git a/webapp/channels/src/components/drafts/draft_title/__snapshots__/draft_title.test.tsx.snap b/webapp/channels/src/components/drafts/draft_title/__snapshots__/draft_title.test.tsx.snap
index 99b1d2ce1d..04a8d3e927 100644
--- a/webapp/channels/src/components/drafts/draft_title/__snapshots__/draft_title.test.tsx.snap
+++ b/webapp/channels/src/components/drafts/draft_title/__snapshots__/draft_title.test.tsx.snap
@@ -1,359 +1,130 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`components/drafts/draft_actions should fetch members when member count is 0 for GM 1`] = `
+
+ To:
+
+ 0
+
+
+
+ Test Channel
+
+
+`;
+
exports[`components/drafts/draft_actions should match snapshot 1`] = `
-
-
+ In:
+
-
+
+
+ Test Channel
+
+
`;
exports[`components/drafts/draft_actions should match snapshot for DM channel 1`] = `
-
-
+ To:
+
-
+
+
+ Test Channel
+
+
`;
exports[`components/drafts/draft_actions should match snapshot for DM channel with teammate 1`] = `
-
-
+ To:
+
-
+
+
+ Test Channel
+
+
`;
exports[`components/drafts/draft_actions should match snapshot for GM channel 1`] = `
-
-
-
+
+ To:
+
+ 5
+
+
+
+ Test Channel
+
+
`;
exports[`components/drafts/draft_actions should match snapshot for open channel 1`] = `
-
-
+ In:
+
-
+
+
+ Test Channel
+
+
`;
exports[`components/drafts/draft_actions should match snapshot for private channel 1`] = `
-
-
+ In:
+
-
+
+
+ Test Channel
+
+
`;
exports[`components/drafts/draft_actions should match snapshot for self draft 1`] = `
-
-
+ In:
+
-
+
+
+ Test Channel
+
+
+ (you)
+
`;
exports[`components/drafts/draft_actions should match snapshot for thread 1`] = `
-
-
+ Thread in:
+
-
+
+
+ Test Channel
+
+
`;
diff --git a/webapp/channels/src/components/drafts/draft_title/draft_title.test.tsx b/webapp/channels/src/components/drafts/draft_title/draft_title.test.tsx
index d2325cb6f8..d98668eb1d 100644
--- a/webapp/channels/src/components/drafts/draft_title/draft_title.test.tsx
+++ b/webapp/channels/src/components/drafts/draft_title/draft_title.test.tsx
@@ -1,102 +1,98 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {shallow} from 'enzyme';
import React from 'react';
-import {Provider} from 'react-redux';
import type {Channel} from '@mattermost/types/channels';
import type {UserProfile} from '@mattermost/types/users';
-import mockStore from 'tests/test_store';
+import {renderWithContext} from 'tests/react_testing_utils';
import Constants from 'utils/constants';
import DraftTitle from './draft_title';
describe('components/drafts/draft_actions', () => {
+ const channel = {
+ type: 'O',
+ display_name: 'Test Channel',
+ } as Channel;
+
const baseProps = {
- channelType: '' as Channel['type'],
- channelName: '',
+ channel,
membersCount: 5,
selfDraft: false,
teammate: {} as UserProfile,
teammateId: '',
- type: '' as 'channel' | 'thread',
+ type: 'channel' as 'channel' | 'thread',
};
it('should match snapshot', () => {
- const store = mockStore();
-
- const wrapper = shallow(
-
-
- ,
+ const {container} = renderWithContext(
+ ,
);
- expect(wrapper).toMatchSnapshot();
+ expect(container).toMatchSnapshot();
});
it('should match snapshot for self draft', () => {
- const store = mockStore();
-
const props = {
...baseProps,
selfDraft: true,
};
- const wrapper = shallow(
-
-
- ,
+ const {container} = renderWithContext(
+ ,
);
- expect(wrapper).toMatchSnapshot();
+ expect(container).toMatchSnapshot();
});
it('should match snapshot for private channel', () => {
- const store = mockStore();
-
+ const channel = {
+ type: Constants.PRIVATE_CHANNEL,
+ display_name: 'Test Channel',
+ } as Channel;
const props = {
...baseProps,
- channelType: Constants.PRIVATE_CHANNEL as Channel['type'],
+ channel,
};
- const wrapper = shallow(
-
-
- ,
+ const {container} = renderWithContext(
+ ,
);
- expect(wrapper).toMatchSnapshot();
+ expect(container).toMatchSnapshot();
});
it('should match snapshot for DM channel', () => {
- const store = mockStore();
-
+ const channel = {
+ type: Constants.DM_CHANNEL,
+ display_name: 'Test Channel',
+ } as Channel;
const props = {
...baseProps,
- channelType: Constants.DM_CHANNEL as Channel['type'],
+ channel,
};
- const wrapper = shallow(
-
-
- ,
+ const {container} = renderWithContext(
+ ,
);
- expect(wrapper).toMatchSnapshot();
+ expect(container).toMatchSnapshot();
});
it('should match snapshot for DM channel with teammate', () => {
- const store = mockStore();
-
+ const channel = {
+ type: Constants.DM_CHANNEL,
+ display_name: 'Test Channel',
+ } as Channel;
const props = {
...baseProps,
- channelType: Constants.DM_CHANNEL as Channel['type'],
+ channel,
teammate: {
username: 'username',
id: 'id',
@@ -104,69 +100,91 @@ describe('components/drafts/draft_actions', () => {
} as UserProfile,
};
- const wrapper = shallow(
-
-
- ,
+ const {container} = renderWithContext(
+ ,
);
- expect(wrapper).toMatchSnapshot();
+ expect(container).toMatchSnapshot();
});
it('should match snapshot for GM channel', () => {
- const store = mockStore();
+ const channel = {
+ type: 'G',
+ display_name: 'Test Channel',
+ } as Channel;
const props = {
...baseProps,
- channelType: Constants.GM_CHANNEL as Channel['type'],
+ channel,
};
- const wrapper = shallow(
-
-
- ,
+ const {container} = renderWithContext(
+ ,
);
- expect(wrapper).toMatchSnapshot();
+ expect(container).toMatchSnapshot();
});
it('should match snapshot for thread', () => {
- const store = mockStore();
+ const channel = {
+ type: Constants.OPEN_CHANNEL,
+ display_name: 'Test Channel',
+ } as Channel;
const props = {
...baseProps,
- channelType: Constants.OPEN_CHANNEL as Channel['type'],
+ channel,
type: 'thread' as 'channel' | 'thread',
};
- const wrapper = shallow(
-
-
- ,
+ const {container} = renderWithContext(
+ ,
);
- expect(wrapper).toMatchSnapshot();
+ expect(container).toMatchSnapshot();
});
it('should match snapshot for open channel', () => {
- const store = mockStore();
+ const channel = {
+ type: Constants.OPEN_CHANNEL,
+ display_name: 'Test Channel',
+ } as Channel;
const props = {
...baseProps,
- channelType: Constants.OPEN_CHANNEL as Channel['type'],
+ channel,
type: 'channel' as 'channel' | 'thread',
};
- const wrapper = shallow(
-
-
- ,
+ const {container} = renderWithContext(
+ ,
);
- expect(wrapper).toMatchSnapshot();
+ expect(container).toMatchSnapshot();
+ });
+
+ it('should fetch members when member count is 0 for GM', () => {
+ const channel = {
+ type: 'G',
+ display_name: 'Test Channel',
+ } as Channel;
+
+ const props = {
+ ...baseProps,
+ channel,
+ membersCount: 0,
+ type: 'channel' as 'channel' | 'thread',
+ };
+
+ const {container} = renderWithContext(
+ ,
+ );
+ expect(container).toMatchSnapshot();
});
});
diff --git a/webapp/channels/src/components/drafts/draft_title/draft_title.tsx b/webapp/channels/src/components/drafts/draft_title/draft_title.tsx
index a0299eb7ff..8f45d6033e 100644
--- a/webapp/channels/src/components/drafts/draft_title/draft_title.tsx
+++ b/webapp/channels/src/components/drafts/draft_title/draft_title.tsx
@@ -8,7 +8,7 @@ import {useDispatch} from 'react-redux';
import type {Channel} from '@mattermost/types/channels';
import type {UserProfile} from '@mattermost/types/users';
-import {getMissingProfilesByIds} from 'mattermost-redux/actions/users';
+import {batchGetProfilesInChannel, getMissingProfilesByIds} from 'mattermost-redux/actions/users';
import Avatar from 'components/widgets/users/avatar';
@@ -18,8 +18,7 @@ import {imageURLForUser} from 'utils/utils';
import './draft_title.scss';
type Props = {
- channelType: Channel['type'];
- channelName: string;
+ channel: Channel;
membersCount?: number;
selfDraft: boolean;
teammate?: UserProfile;
@@ -28,8 +27,7 @@ type Props = {
}
function DraftTitle({
- channelType,
- channelName,
+ channel,
membersCount,
selfDraft,
teammate,
@@ -44,6 +42,19 @@ function DraftTitle({
}
}, [teammate?.id, teammateId]);
+ useEffect(() => {
+ // if you have a scheduled post in a GM and you closed that GM,
+ // we don't fetch that GM's members by default. This causes the number of GM members
+ // in scheduled posts row header to show up as '0'. To fix this,
+ // we check if the channel is a GM and member count is 0 (could will at least be 1 as the current user
+ // is always a member) and if so, fetch the GM members.
+ // The action uses a data loader so it is safe to call do this for multiple
+ // scheduled posts for the same GM without causing any duplicate API calls.
+ if (channel.type === Constants.GM_CHANNEL && !membersCount) {
+ dispatch(batchGetProfilesInChannel(channel.id));
+ }
+ }, [channel.id, channel.type, dispatch, membersCount]);
+
let you = null;
let title = null;
@@ -61,11 +72,11 @@ function DraftTitle({
let icon = ;
- if (channelType === Constants.PRIVATE_CHANNEL) {
+ if (channel.type === Constants.PRIVATE_CHANNEL) {
icon = ;
}
- if (channelType === Constants.DM_CHANNEL && teammate) {
+ if (channel.type === Constants.DM_CHANNEL && teammate) {
icon = (
{membersCount}
@@ -86,8 +97,8 @@ function DraftTitle({
if (type === 'thread') {
if (
- channelType !== Constants.GM_CHANNEL &&
- channelType !== Constants.DM_CHANNEL
+ channel.type !== Constants.GM_CHANNEL &&
+ channel.type !== Constants.DM_CHANNEL
) {
title = (
{channelName}'}
values={{
icon,
- channelName,
+ channelName: channel.display_name,
span: (chunks: React.ReactNode) => ({chunks}),
}}
/>
@@ -107,15 +118,15 @@ function DraftTitle({
defaultMessage={'Thread to: {icon} {channelName}'}
values={{
icon,
- channelName,
+ channelName: channel.display_name,
span: (chunks: React.ReactNode) => ({chunks}),
}}
/>
);
}
} else if (
- channelType !== Constants.GM_CHANNEL &&
- channelType !== Constants.DM_CHANNEL
+ channel.type !== Constants.GM_CHANNEL &&
+ channel.type !== Constants.DM_CHANNEL
) {
title = (
{channelName}'}
values={{
icon,
- channelName,
+ channelName: channel.display_name,
span: (chunks: React.ReactNode) => ({chunks}),
}}
/>
@@ -135,7 +146,7 @@ function DraftTitle({
defaultMessage={'To: {icon} {channelName}'}
values={{
icon,
- channelName,
+ channelName: channel.display_name,
span: (chunks: React.ReactNode) => ({chunks}),
}}
/>
diff --git a/webapp/channels/src/components/drafts/draft_title/index.ts b/webapp/channels/src/components/drafts/draft_title/index.ts
index f823c08181..83b321c98f 100644
--- a/webapp/channels/src/components/drafts/draft_title/index.ts
+++ b/webapp/channels/src/components/drafts/draft_title/index.ts
@@ -25,7 +25,6 @@ function makeMapStateToProps() {
return (state: GlobalState, ownProps: OwnProps) => {
const {channel, userId} = ownProps;
- const channelName = channel.display_name;
let teammateId;
let teammate;
let membersCount;
@@ -40,8 +39,7 @@ function makeMapStateToProps() {
}
return {
- channelName,
- channelType: channel.type,
+ channel,
membersCount,
selfDraft: teammateId === userId,
teammate,
diff --git a/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts b/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts
index 83e9232856..6a4df7a39b 100644
--- a/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts
+++ b/webapp/channels/src/packages/mattermost-redux/src/actions/users.ts
@@ -5,6 +5,7 @@ import type {AnyAction} from 'redux';
import {batchActions} from 'redux-batched-actions';
import type {UserAutocomplete} from '@mattermost/types/autocomplete';
+import type {Channel} from '@mattermost/types/channels';
import type {ServerError} from '@mattermost/types/errors';
import type {UserProfile, UserStatus, GetFilteredUsersStatsOpts, UsersStats, UserCustomStatus, UserAccessToken} from '@mattermost/types/users';
@@ -375,6 +376,21 @@ export function getProfilesInChannel(channelId: string, page: number, perPage: n
};
}
+export function batchGetProfilesInChannel(channelId: string): ActionFuncAsync> {
+ return async (dispatch, getState, {loaders}: any) => {
+ if (!loaders.profilesInChannelLoader) {
+ loaders.profilesInChannelLoader = new DelayedDataLoader({
+ fetchBatch: (channelIds) => dispatch(getProfilesInChannel(channelIds[0], 0)),
+ maxBatchSize: 1,
+ wait: missingProfilesWait,
+ });
+ }
+
+ await loaders.profilesInChannelLoader.queueAndWait([channelId]);
+ return {};
+ };
+}
+
export function getProfilesInGroupChannels(channelsIds: string[]): ActionFuncAsync {
return async (dispatch, getState) => {
let channelProfiles;
diff --git a/webapp/channels/src/utils/test_intl.tsx b/webapp/channels/src/utils/test_intl.tsx
new file mode 100644
index 0000000000..7ae98ffd5b
--- /dev/null
+++ b/webapp/channels/src/utils/test_intl.tsx
@@ -0,0 +1,14 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import type {ReactNode} from 'react';
+import React from 'react';
+import {IntlProvider, createIntl} from 'react-intl';
+
+export const defaultIntl = createIntl({
+ locale: 'en',
+ defaultLocale: 'en',
+ messages: {},
+});
+
+export const wrapIntl = (children?: ReactNode) => {children};