[MM-45272] Fix MM-45272 (#24701)
* Fix MM-45272 * Properly handle permalinks * Fix * Fix tests * Handle only not found case for team member * Fix lint * Use proper config value * Separate permission in several statements * Add tests * Fix lint * Revert changes on utils * Address feedback and more fixes * Address feedback * Fix test * Fix test and related bug * Fix and reorder test * Address feedback * Address feedback --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c395ec6245
Коммит
2ff0fe343e
@@ -7,7 +7,8 @@ import type {Post} from '@mattermost/types/posts';
|
||||
import {getChannel, getChannelMember, selectChannel, joinChannel, getChannelStats} from 'mattermost-redux/actions/channels';
|
||||
import {getPostThread} from 'mattermost-redux/actions/posts';
|
||||
import {getMissingProfilesByIds} from 'mattermost-redux/actions/users';
|
||||
import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {getCurrentChannel, getChannel as getChannelFromRedux} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeam, getTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
|
||||
@@ -88,25 +89,48 @@ export function focusPost(postId: string, returnTo = '', currentUserId: string,
|
||||
if (privateChannelJoinPromptVisible) {
|
||||
return;
|
||||
}
|
||||
const {data} = await dispatch(getPostThread(postId));
|
||||
|
||||
if (!data) {
|
||||
let postInfo;
|
||||
try {
|
||||
postInfo = await Client4.getPostInfo(postId);
|
||||
} catch (e) {
|
||||
getHistory().replace(`/error?type=${ErrorPageTypes.PERMALINK_NOT_FOUND}&returnTo=${returnTo}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.first_inaccessible_post_time) {
|
||||
const state = getState();
|
||||
const currentTeam = getCurrentTeam(state);
|
||||
|
||||
if (!postInfo.has_joined_channel) {
|
||||
// Prompt system admin before joining the private channel
|
||||
const user = getCurrentUser(state);
|
||||
if (postInfo.channel_type === Constants.PRIVATE_CHANNEL && isSystemAdmin(user.roles)) {
|
||||
privateChannelJoinPromptVisible = true;
|
||||
const joinPromptResult = await dispatch(joinPrivateChannelPrompt(currentTeam, postInfo.channel_display_name));
|
||||
privateChannelJoinPromptVisible = false;
|
||||
if ('data' in joinPromptResult && !joinPromptResult.data.join) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
await dispatch(joinChannel(currentUserId, '', postInfo.channel_id));
|
||||
}
|
||||
|
||||
const {data: threadData} = await dispatch(getPostThread(postId));
|
||||
|
||||
if (!threadData) {
|
||||
getHistory().replace(`/error?type=${ErrorPageTypes.PERMALINK_NOT_FOUND}&returnTo=${returnTo}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (threadData.first_inaccessible_post_time) {
|
||||
getHistory().replace(`/error?type=${ErrorPageTypes.CLOUD_ARCHIVED}&returnTo=${returnTo}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
const isCollapsed = isCollapsedThreadsEnabled(state);
|
||||
|
||||
const channelId = data.posts[data.order[0]].channel_id;
|
||||
let channel = state.entities.channels.channels[channelId];
|
||||
const currentTeam = getCurrentTeam(state);
|
||||
const teamId = currentTeam.id;
|
||||
const channelId = threadData.posts[threadData.order[0]].channel_id;
|
||||
let channel = getChannelFromRedux(state, channelId);
|
||||
|
||||
if (!channel) {
|
||||
const {data: channelData} = await dispatch(getChannel(channelId));
|
||||
@@ -119,6 +143,7 @@ export function focusPost(postId: string, returnTo = '', currentUserId: string,
|
||||
channel = channelData;
|
||||
}
|
||||
|
||||
const teamId = channel.team_id || currentTeam.id;
|
||||
let myMember = state.entities.channels.myMembers[channelId];
|
||||
|
||||
if (!myMember) {
|
||||
@@ -134,17 +159,8 @@ export function focusPost(postId: string, returnTo = '', currentUserId: string,
|
||||
}
|
||||
|
||||
if (!myMember) {
|
||||
// Prompt system admin before joining the private channel
|
||||
const user = getCurrentUser(state);
|
||||
if (channel.type === Constants.PRIVATE_CHANNEL && isSystemAdmin(user.roles)) {
|
||||
privateChannelJoinPromptVisible = true;
|
||||
const joinPromptResult = await dispatch(joinPrivateChannelPrompt(currentTeam, channel));
|
||||
privateChannelJoinPromptVisible = false;
|
||||
if ('data' in joinPromptResult && !joinPromptResult.data.join) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
await dispatch(joinChannel(currentUserId, '', channelId, channel.name));
|
||||
getHistory().replace(`/error?type=${ErrorPageTypes.PERMALINK_NOT_FOUND}&returnTo=${returnTo}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +177,7 @@ export function focusPost(postId: string, returnTo = '', currentUserId: string,
|
||||
dispatch(loadNewGMIfNeeded(channel.id));
|
||||
}
|
||||
|
||||
const post = data.posts[postId];
|
||||
const post = threadData.posts[postId];
|
||||
|
||||
if (isCollapsed && isComment(post)) {
|
||||
const {data} = await dispatch(focusReplyPost(post, channel, teamId, returnTo, option));
|
||||
|
||||
@@ -174,24 +174,46 @@ describe('components/PermalinkView', () => {
|
||||
};
|
||||
|
||||
describe('focusPost', () => {
|
||||
test('should redirect to error page for DM channel not a member of', async () => {
|
||||
const testStore = await mockStore(initialState);
|
||||
await testStore.dispatch(focusPost('dmpostid1', undefined, baseProps.currentUserId));
|
||||
beforeEach(() => {
|
||||
TestHelper.initBasic(Client4);
|
||||
});
|
||||
|
||||
expect(getPostThread).toHaveBeenCalledWith('dmpostid1');
|
||||
afterEach(() => {
|
||||
TestHelper.tearDown();
|
||||
});
|
||||
|
||||
function nockInfoForPost(postId: string) {
|
||||
nock(Client4.getPostRoute(postId)).
|
||||
get('/info').
|
||||
reply(200, {
|
||||
has_joined_channel: true,
|
||||
});
|
||||
}
|
||||
test('should redirect to error page for DM channel not a member of', async () => {
|
||||
const postId = 'dmpostid1';
|
||||
TestHelper.initBasic(Client4);
|
||||
nockInfoForPost(postId);
|
||||
|
||||
const testStore = await mockStore(initialState);
|
||||
await testStore.dispatch(focusPost(postId, undefined, baseProps.currentUserId));
|
||||
|
||||
expect(getPostThread).toHaveBeenCalledWith(postId);
|
||||
expect(testStore.getActions()).toEqual([
|
||||
{type: 'MOCK_GET_POST_THREAD', data: {posts: {dmpostid1: {id: 'dmpostid1', message: 'some message', channel_id: 'dmchannelid'}}, order: ['dmpostid1']}},
|
||||
{type: 'MOCK_GET_POST_THREAD', data: {posts: {dmpostid1: {id: postId, message: 'some message', channel_id: 'dmchannelid'}}, order: ['dmpostid1']}},
|
||||
]);
|
||||
expect(getHistory().replace).toHaveBeenCalledWith(`/error?type=${ErrorPageTypes.PERMALINK_NOT_FOUND}&returnTo=`);
|
||||
});
|
||||
|
||||
test('should redirect to error page for GM channel not a member of', async () => {
|
||||
const testStore = await mockStore(initialState);
|
||||
await testStore.dispatch(focusPost('gmpostid1', undefined, baseProps.currentUserId));
|
||||
const postId = 'gmpostid1';
|
||||
nockInfoForPost(postId);
|
||||
|
||||
expect(getPostThread).toHaveBeenCalledWith('gmpostid1');
|
||||
const testStore = await mockStore(initialState);
|
||||
await testStore.dispatch(focusPost(postId, undefined, baseProps.currentUserId));
|
||||
|
||||
expect(getPostThread).toHaveBeenCalledWith(postId);
|
||||
expect(testStore.getActions()).toEqual([
|
||||
{type: 'MOCK_GET_POST_THREAD', data: {posts: {gmpostid1: {id: 'gmpostid1', message: 'some message', channel_id: 'gmchannelid'}}, order: ['gmpostid1']}},
|
||||
{type: 'MOCK_GET_POST_THREAD', data: {posts: {gmpostid1: {id: postId, message: 'some message', channel_id: 'gmchannelid'}}, order: ['gmpostid1']}},
|
||||
]);
|
||||
expect(getHistory().replace).toHaveBeenCalledWith(`/error?type=${ErrorPageTypes.PERMALINK_NOT_FOUND}&returnTo=`);
|
||||
});
|
||||
@@ -199,7 +221,9 @@ describe('components/PermalinkView', () => {
|
||||
test('should redirect to DM link with postId for permalink', async () => {
|
||||
const dateNowOrig = Date.now;
|
||||
Date.now = () => new Date(0).getMilliseconds();
|
||||
const nextTick = () => new Promise((res) => process.nextTick(res));
|
||||
|
||||
const postId = 'dmpostid1';
|
||||
nockInfoForPost(postId);
|
||||
|
||||
TestHelper.initBasic(Client4);
|
||||
nock(Client4.getUsersRoute()).
|
||||
@@ -220,13 +244,12 @@ describe('components/PermalinkView', () => {
|
||||
};
|
||||
|
||||
const testStore = mockStore(modifiedState);
|
||||
testStore.dispatch(focusPost('dmpostid1', undefined, baseProps.currentUserId));
|
||||
await testStore.dispatch(focusPost(postId, undefined, baseProps.currentUserId));
|
||||
|
||||
await nextTick();
|
||||
expect.assertions(3);
|
||||
expect(getPostThread).toHaveBeenCalledWith('dmpostid1');
|
||||
expect(getPostThread).toHaveBeenCalledWith(postId);
|
||||
expect(testStore.getActions()).toEqual([
|
||||
{type: 'MOCK_GET_POST_THREAD', data: {posts: {dmpostid1: {id: 'dmpostid1', message: 'some message', channel_id: 'dmchannelid'}}, order: ['dmpostid1']}},
|
||||
{type: 'MOCK_GET_POST_THREAD', data: {posts: {dmpostid1: {id: postId, message: 'some message', channel_id: 'dmchannelid'}}, order: [postId]}},
|
||||
{type: 'MOCK_GET_MISSING_PROFILES', userIds: ['dmchannel']},
|
||||
{
|
||||
type: 'RECEIVED_PREFERENCES',
|
||||
@@ -236,17 +259,19 @@ describe('components/PermalinkView', () => {
|
||||
],
|
||||
},
|
||||
{type: 'MOCK_SELECT_CHANNEL', args: ['dmchannelid']},
|
||||
{type: 'RECEIVED_FOCUSED_POST', channelId: 'dmchannelid', data: 'dmpostid1'},
|
||||
{type: 'RECEIVED_FOCUSED_POST', channelId: 'dmchannelid', data: postId},
|
||||
{type: 'MOCK_LOAD_CHANNELS_FOR_CURRENT_USER'},
|
||||
{type: 'MOCK_GET_CHANNEL_STATS', args: ['dmchannelid']},
|
||||
]);
|
||||
|
||||
expect(getHistory().replace).toHaveBeenCalledWith('/currentteam/messages/@otherUser/dmpostid1');
|
||||
Date.now = dateNowOrig;
|
||||
TestHelper.tearDown();
|
||||
});
|
||||
|
||||
test('should redirect to GM link with postId for permalink', async () => {
|
||||
const postId = 'gmpostid1';
|
||||
nockInfoForPost(postId);
|
||||
|
||||
const modifiedState = {
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
@@ -261,13 +286,13 @@ describe('components/PermalinkView', () => {
|
||||
};
|
||||
|
||||
const testStore = await mockStore(modifiedState);
|
||||
await testStore.dispatch(focusPost('gmpostid1', undefined, baseProps.currentUserId));
|
||||
await testStore.dispatch(focusPost(postId, undefined, baseProps.currentUserId));
|
||||
|
||||
expect(getPostThread).toHaveBeenCalledWith('gmpostid1');
|
||||
expect(getPostThread).toHaveBeenCalledWith(postId);
|
||||
expect(testStore.getActions()).toEqual([
|
||||
{type: 'MOCK_GET_POST_THREAD', data: {posts: {gmpostid1: {id: 'gmpostid1', message: 'some message', channel_id: 'gmchannelid'}}, order: ['gmpostid1']}},
|
||||
{type: 'MOCK_GET_POST_THREAD', data: {posts: {gmpostid1: {id: postId, message: 'some message', channel_id: 'gmchannelid'}}, order: [postId]}},
|
||||
{type: 'MOCK_SELECT_CHANNEL', args: ['gmchannelid']},
|
||||
{type: 'RECEIVED_FOCUSED_POST', channelId: 'gmchannelid', data: 'gmpostid1'},
|
||||
{type: 'RECEIVED_FOCUSED_POST', channelId: 'gmchannelid', data: postId},
|
||||
{type: 'MOCK_LOAD_CHANNELS_FOR_CURRENT_USER'},
|
||||
{type: 'MOCK_GET_CHANNEL_STATS', args: ['gmchannelid']},
|
||||
]);
|
||||
@@ -275,23 +300,26 @@ describe('components/PermalinkView', () => {
|
||||
});
|
||||
|
||||
test('should redirect to channel link with postId for permalink', async () => {
|
||||
const testStore = await mockStore(initialState);
|
||||
await testStore.dispatch(focusPost('postid1', undefined, baseProps.currentUserId));
|
||||
const postId = 'postid1';
|
||||
nockInfoForPost(postId);
|
||||
|
||||
expect(getPostThread).toHaveBeenCalledWith('postid1');
|
||||
const testStore = await mockStore(initialState);
|
||||
await testStore.dispatch(focusPost(postId, undefined, baseProps.currentUserId));
|
||||
|
||||
expect(getPostThread).toHaveBeenCalledWith(postId);
|
||||
expect(testStore.getActions()).toEqual([
|
||||
{
|
||||
type: 'MOCK_GET_POST_THREAD',
|
||||
data: {
|
||||
posts: {
|
||||
replypostid1: {id: 'replypostid1', message: 'some message', channel_id: 'channelid1', root_id: 'postid1'},
|
||||
postid1: {id: 'postid1', message: 'some message', channel_id: 'channelid1'},
|
||||
replypostid1: {id: 'replypostid1', message: 'some message', channel_id: 'channelid1', root_id: postId},
|
||||
postid1: {id: postId, message: 'some message', channel_id: 'channelid1'},
|
||||
},
|
||||
order: ['postid1', 'replypostid1'],
|
||||
order: [postId, 'replypostid1'],
|
||||
},
|
||||
},
|
||||
{type: 'MOCK_SELECT_CHANNEL', args: ['channelid1']},
|
||||
{type: 'RECEIVED_FOCUSED_POST', channelId: 'channelid1', data: 'postid1'},
|
||||
{type: 'RECEIVED_FOCUSED_POST', channelId: 'channelid1', data: postId},
|
||||
{type: 'MOCK_LOAD_CHANNELS_FOR_CURRENT_USER'},
|
||||
{type: 'MOCK_GET_CHANNEL_STATS', args: ['channelid1']},
|
||||
]);
|
||||
@@ -299,6 +327,9 @@ describe('components/PermalinkView', () => {
|
||||
});
|
||||
|
||||
test('should not redirect to channel link with postId for a reply permalink when collapsedThreads enabled and option is set true', async () => {
|
||||
const postId = 'replypostid1';
|
||||
nockInfoForPost(postId);
|
||||
|
||||
const newState = {
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
@@ -313,34 +344,34 @@ describe('components/PermalinkView', () => {
|
||||
jest.spyOn<typeof Channels, keyof typeof Channels>(Channels, 'getCurrentChannel').mockReturnValue({id: 'channelid1', name: 'channel1', type: 'O', team_id: 'current_team_id'});
|
||||
|
||||
const testStore = await mockStore(newState);
|
||||
await testStore.dispatch(focusPost('replypostid1', '#', initialState.entities.users.currentUserId, {skipRedirectReplyPermalink: true}));
|
||||
await testStore.dispatch(focusPost(postId, '#', initialState.entities.users.currentUserId, {skipRedirectReplyPermalink: true}));
|
||||
|
||||
expect(getPostThread).toHaveBeenCalledWith('replypostid1');
|
||||
expect(getPostThread).toHaveBeenCalledWith(postId);
|
||||
|
||||
expect(testStore.getActions()).toEqual([
|
||||
{
|
||||
type: 'MOCK_GET_POST_THREAD',
|
||||
data: {
|
||||
posts: {
|
||||
replypostid1: {id: 'replypostid1', message: 'some message', channel_id: 'channelid1', root_id: 'postid1'},
|
||||
replypostid1: {id: postId, message: 'some message', channel_id: 'channelid1', root_id: 'postid1'},
|
||||
postid1: {id: 'postid1', message: 'some message', channel_id: 'channelid1'},
|
||||
|
||||
},
|
||||
order: ['postid1', 'replypostid1'],
|
||||
order: ['postid1', postId],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'MOCK_GET_POST_THREAD',
|
||||
data: {
|
||||
posts: {
|
||||
replypostid1: {id: 'replypostid1', message: 'some message', channel_id: 'channelid1', root_id: 'postid1'},
|
||||
replypostid1: {id: postId, message: 'some message', channel_id: 'channelid1', root_id: 'postid1'},
|
||||
postid1: {id: 'postid1', message: 'some message', channel_id: 'channelid1'},
|
||||
|
||||
},
|
||||
order: ['postid1', 'replypostid1'],
|
||||
order: ['postid1', postId],
|
||||
},
|
||||
},
|
||||
{type: 'MOCK_SELECT_POST_AND_HIGHLIGHT', args: [{id: 'replypostid1', message: 'some message', channel_id: 'channelid1', root_id: 'postid1'}]},
|
||||
{type: 'MOCK_SELECT_POST_AND_HIGHLIGHT', args: [{id: postId, message: 'some message', channel_id: 'channelid1', root_id: 'postid1'}]},
|
||||
{type: 'MOCK_LOAD_CHANNELS_FOR_CURRENT_USER'},
|
||||
{type: 'MOCK_GET_CHANNEL_STATS', args: ['channelid1']},
|
||||
]);
|
||||
|
||||
Ссылка в новой задаче
Block a user