[MM-42586] Reaction toggle behavior (#25412)
* add toggle_reaction action * change to use toggle_reaction action instead of add_reaction * add submit_reaction action * add a selector to check if a reaction has already been added * update test --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
2184876c77
Коммит
34ce0d00d4
@@ -12,10 +12,12 @@ import * as Actions from 'actions/post_actions';
|
|||||||
|
|
||||||
import mockStore from 'tests/test_store';
|
import mockStore from 'tests/test_store';
|
||||||
import {Constants, ActionTypes, RHSStates} from 'utils/constants';
|
import {Constants, ActionTypes, RHSStates} from 'utils/constants';
|
||||||
|
import * as PostUtils from 'utils/post_utils';
|
||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
import type {GlobalState} from 'types/store';
|
||||||
|
|
||||||
jest.mock('mattermost-redux/actions/posts', () => ({
|
jest.mock('mattermost-redux/actions/posts', () => ({
|
||||||
|
removeReaction: (...args: any[]) => ({type: 'MOCK_REMOVE_REACTION', args}),
|
||||||
addReaction: (...args: any[]) => ({type: 'MOCK_ADD_REACTION', args}),
|
addReaction: (...args: any[]) => ({type: 'MOCK_ADD_REACTION', args}),
|
||||||
createPost: (...args: any[]) => ({type: 'MOCK_CREATE_POST', args}),
|
createPost: (...args: any[]) => ({type: 'MOCK_CREATE_POST', args}),
|
||||||
createPostImmediately: (...args: any[]) => ({type: 'MOCK_CREATE_POST_IMMEDIATELY', args}),
|
createPostImmediately: (...args: any[]) => ({type: 'MOCK_CREATE_POST_IMMEDIATELY', args}),
|
||||||
@@ -48,6 +50,8 @@ jest.mock('utils/user_agent', () => ({
|
|||||||
isDesktopApp: jest.fn().mockReturnValue(false),
|
isDesktopApp: jest.fn().mockReturnValue(false),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const mockMakeGetIsReactionAlreadyAddedToPost = jest.spyOn(PostUtils, 'makeGetIsReactionAlreadyAddedToPost');
|
||||||
|
|
||||||
const POST_CREATED_TIME = Date.now();
|
const POST_CREATED_TIME = Date.now();
|
||||||
|
|
||||||
// This mocks the Date.now() function so it returns a constant value
|
// This mocks the Date.now() function so it returns a constant value
|
||||||
@@ -433,6 +437,84 @@ describe('Actions.Posts', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('submitReaction', () => {
|
||||||
|
describe('addReaction', () => {
|
||||||
|
test('should add reaction when the action is + and the reaction is not added', async () => {
|
||||||
|
const testStore = mockStore(initialState);
|
||||||
|
|
||||||
|
mockMakeGetIsReactionAlreadyAddedToPost.mockReturnValueOnce(() => false);
|
||||||
|
|
||||||
|
testStore.dispatch(Actions.submitReaction('post_id_1', '+', 'emoji_name_1'));
|
||||||
|
|
||||||
|
expect(testStore.getActions()).toEqual([
|
||||||
|
{args: ['post_id_1', 'emoji_name_1'], type: 'MOCK_ADD_REACTION'},
|
||||||
|
{args: ['emoji_name_1'], type: 'MOCK_ADD_RECENT_EMOJI'},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should take no action when the action is + and the reaction has already been added', async () => {
|
||||||
|
const testStore = mockStore(initialState);
|
||||||
|
|
||||||
|
mockMakeGetIsReactionAlreadyAddedToPost.mockReturnValueOnce(() => true);
|
||||||
|
|
||||||
|
testStore.dispatch(Actions.submitReaction('post_id_1', '+', 'emoji_name_1'));
|
||||||
|
|
||||||
|
expect(testStore.getActions()).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removeReaction', () => {
|
||||||
|
test('should remove reaction when the action is - and the reaction has already been added', async () => {
|
||||||
|
const testStore = mockStore(initialState);
|
||||||
|
|
||||||
|
mockMakeGetIsReactionAlreadyAddedToPost.mockReturnValueOnce(() => true);
|
||||||
|
|
||||||
|
testStore.dispatch(Actions.submitReaction('post_id_1', '-', 'emoji_name_1'));
|
||||||
|
|
||||||
|
expect(testStore.getActions()).toEqual([
|
||||||
|
{args: ['post_id_1', 'emoji_name_1'], type: 'MOCK_REMOVE_REACTION'},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should take no action when the action is - and the reaction is not added', async () => {
|
||||||
|
const testStore = mockStore(initialState);
|
||||||
|
|
||||||
|
mockMakeGetIsReactionAlreadyAddedToPost.mockReturnValueOnce(() => false);
|
||||||
|
|
||||||
|
testStore.dispatch(Actions.submitReaction('post_id_1', '-', 'emoji_name_1'));
|
||||||
|
|
||||||
|
expect(testStore.getActions()).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toggleReaction', () => {
|
||||||
|
test('should add reaction when the reaction is not added', async () => {
|
||||||
|
const testStore = mockStore(initialState);
|
||||||
|
|
||||||
|
mockMakeGetIsReactionAlreadyAddedToPost.mockReturnValueOnce(() => false);
|
||||||
|
|
||||||
|
testStore.dispatch(Actions.toggleReaction('post_id_1', 'emoji_name_1'));
|
||||||
|
|
||||||
|
expect(testStore.getActions()).toEqual([
|
||||||
|
{args: ['post_id_1', 'emoji_name_1'], type: 'MOCK_ADD_REACTION'},
|
||||||
|
{args: ['emoji_name_1'], type: 'MOCK_ADD_RECENT_EMOJI'},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should remove reaction when the reaction has already been added', async () => {
|
||||||
|
const testStore = mockStore(initialState);
|
||||||
|
|
||||||
|
mockMakeGetIsReactionAlreadyAddedToPost.mockReturnValueOnce(() => true);
|
||||||
|
|
||||||
|
testStore.dispatch(Actions.toggleReaction('post_id_1', 'emoji_name_1'));
|
||||||
|
|
||||||
|
expect(testStore.getActions()).toEqual([
|
||||||
|
{args: ['post_id_1', 'emoji_name_1'], type: 'MOCK_REMOVE_REACTION'},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('addReaction', async () => {
|
test('addReaction', async () => {
|
||||||
const testStore = mockStore(initialState);
|
const testStore = mockStore(initialState);
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import {
|
|||||||
StoragePrefixes,
|
StoragePrefixes,
|
||||||
} from 'utils/constants';
|
} from 'utils/constants';
|
||||||
import {matchEmoticons} from 'utils/emoticons';
|
import {matchEmoticons} from 'utils/emoticons';
|
||||||
|
import {makeGetIsReactionAlreadyAddedToPost} from 'utils/post_utils';
|
||||||
import * as UserAgent from 'utils/user_agent';
|
import * as UserAgent from 'utils/user_agent';
|
||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
import type {GlobalState} from 'types/store';
|
||||||
@@ -140,6 +141,36 @@ function storeCommentDraft(rootPostId: string, draft: null) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function submitReaction(postId: string, action: string, emojiName: string) {
|
||||||
|
return (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||||
|
const state = getState() as GlobalState;
|
||||||
|
const getIsReactionAlreadyAddedToPost = makeGetIsReactionAlreadyAddedToPost();
|
||||||
|
|
||||||
|
const isReactionAlreadyAddedToPost = getIsReactionAlreadyAddedToPost(state, postId, emojiName);
|
||||||
|
|
||||||
|
if (action === '+' && !isReactionAlreadyAddedToPost) {
|
||||||
|
dispatch(addReaction(postId, emojiName));
|
||||||
|
} else if (action === '-' && isReactionAlreadyAddedToPost) {
|
||||||
|
dispatch(PostActions.removeReaction(postId, emojiName));
|
||||||
|
}
|
||||||
|
return {data: true};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleReaction(postId: string, emojiName: string) {
|
||||||
|
return (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||||
|
const state = getState() as GlobalState;
|
||||||
|
const getIsReactionAlreadyAddedToPost = makeGetIsReactionAlreadyAddedToPost();
|
||||||
|
|
||||||
|
const isReactionAlreadyAddedToPost = getIsReactionAlreadyAddedToPost(state, postId, emojiName);
|
||||||
|
|
||||||
|
if (isReactionAlreadyAddedToPost) {
|
||||||
|
return dispatch(PostActions.removeReaction(postId, emojiName));
|
||||||
|
}
|
||||||
|
return dispatch(addReaction(postId, emojiName));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function addReaction(postId: string, emojiName: string) {
|
export function addReaction(postId: string, emojiName: string) {
|
||||||
return (dispatch: DispatchFunc) => {
|
return (dispatch: DispatchFunc) => {
|
||||||
dispatch(PostActions.addReaction(postId, emojiName));
|
dispatch(PostActions.addReaction(postId, emojiName));
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {
|
import {
|
||||||
removeReaction,
|
|
||||||
addMessageIntoHistory,
|
addMessageIntoHistory,
|
||||||
moveHistoryIndexBack,
|
moveHistoryIndexBack,
|
||||||
} from 'mattermost-redux/actions/posts';
|
} from 'mattermost-redux/actions/posts';
|
||||||
@@ -17,7 +16,6 @@ import {
|
|||||||
updateCommentDraft,
|
updateCommentDraft,
|
||||||
makeOnMoveHistoryIndex,
|
makeOnMoveHistoryIndex,
|
||||||
submitPost,
|
submitPost,
|
||||||
submitReaction,
|
|
||||||
submitCommand,
|
submitCommand,
|
||||||
makeOnSubmit,
|
makeOnSubmit,
|
||||||
makeOnEditLatestPost,
|
makeOnEditLatestPost,
|
||||||
@@ -63,6 +61,7 @@ jest.mock('actions/hooks', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('actions/post_actions', () => ({
|
jest.mock('actions/post_actions', () => ({
|
||||||
|
submitReaction: (...args) => ({type: 'MOCK_SUBMIT_REACTION', args}),
|
||||||
addReaction: (...args) => ({type: 'MOCK_ADD_REACTION', args}),
|
addReaction: (...args) => ({type: 'MOCK_ADD_REACTION', args}),
|
||||||
createPost: jest.fn(() => ({type: 'MOCK_CREATE_POST'})),
|
createPost: jest.fn(() => ({type: 'MOCK_CREATE_POST'})),
|
||||||
setEditingPost: (...args) => ({type: 'MOCK_SET_EDITING_POST', args}),
|
setEditingPost: (...args) => ({type: 'MOCK_SET_EDITING_POST', args}),
|
||||||
@@ -293,25 +292,6 @@ describe('rhs view actions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('submitReaction', () => {
|
|
||||||
test('it adds a reaction when action is +', () => {
|
|
||||||
store.dispatch(submitReaction('post_id_1', '+', 'emoji_name_1'));
|
|
||||||
|
|
||||||
const testStore = mockStore(initialState);
|
|
||||||
testStore.dispatch(PostActions.addReaction('post_id_1', 'emoji_name_1'));
|
|
||||||
expect(store.getActions()).toEqual(testStore.getActions());
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it removes a reaction when action is -', () => {
|
|
||||||
store.dispatch(submitReaction('post_id_1', '-', 'emoji_name_1'));
|
|
||||||
|
|
||||||
const testStore = mockStore(initialState);
|
|
||||||
testStore.dispatch(removeReaction('post_id_1', 'emoji_name_1'));
|
|
||||||
|
|
||||||
expect(store.getActions()).toEqual(testStore.getActions());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('submitCommand', () => {
|
describe('submitCommand', () => {
|
||||||
const args = {
|
const args = {
|
||||||
channel_id: channelId,
|
channel_id: channelId,
|
||||||
@@ -400,7 +380,7 @@ describe('rhs view actions', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const testStore = mockStore(initialState);
|
const testStore = mockStore(initialState);
|
||||||
testStore.dispatch(submitReaction(latestPostId, '+', 'smile'));
|
testStore.dispatch(PostActions.submitReaction(latestPostId, '+', 'smile'));
|
||||||
|
|
||||||
expect(store.getActions()).toEqual(
|
expect(store.getActions()).toEqual(
|
||||||
expect.arrayContaining(testStore.getActions()),
|
expect.arrayContaining(testStore.getActions()),
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import type {Post} from '@mattermost/types/posts';
|
import type {Post} from '@mattermost/types/posts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
removeReaction,
|
|
||||||
addMessageIntoHistory,
|
addMessageIntoHistory,
|
||||||
moveHistoryIndexBack,
|
moveHistoryIndexBack,
|
||||||
moveHistoryIndexForward,
|
moveHistoryIndexForward,
|
||||||
@@ -106,17 +105,6 @@ export function submitPost(channelId: string, rootId: string, draft: PostDraft)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function submitReaction(postId: string, action: string, emojiName: string) {
|
|
||||||
return (dispatch: DispatchFunc) => {
|
|
||||||
if (action === '+') {
|
|
||||||
dispatch(PostActions.addReaction(postId, emojiName));
|
|
||||||
} else if (action === '-') {
|
|
||||||
dispatch(removeReaction(postId, emojiName));
|
|
||||||
}
|
|
||||||
return {data: true};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function submitCommand(channelId: string, rootId: string, draft: PostDraft) {
|
export function submitCommand(channelId: string, rootId: string, draft: PostDraft) {
|
||||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
@@ -170,7 +158,7 @@ export function makeOnSubmit(channelId: string, rootId: string, latestPostId: st
|
|||||||
const emojiMap = new EmojiMap(emojis);
|
const emojiMap = new EmojiMap(emojis);
|
||||||
|
|
||||||
if (isReaction && emojiMap.has(isReaction[2])) {
|
if (isReaction && emojiMap.has(isReaction[2])) {
|
||||||
dispatch(submitReaction(latestPostId, isReaction[1], isReaction[2]));
|
dispatch(PostActions.submitReaction(latestPostId, isReaction[1], isReaction[2]));
|
||||||
} else if (message.indexOf('/') === 0 && !options.ignoreSlash) {
|
} else if (message.indexOf('/') === 0 && !options.ignoreSlash) {
|
||||||
try {
|
try {
|
||||||
await dispatch(submitCommand(channelId, rootId, draft));
|
await dispatch(submitCommand(channelId, rootId, draft));
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ const baseProp: Props = {
|
|||||||
addMessageIntoHistory: jest.fn(),
|
addMessageIntoHistory: jest.fn(),
|
||||||
moveHistoryIndexBack: jest.fn(),
|
moveHistoryIndexBack: jest.fn(),
|
||||||
moveHistoryIndexForward: jest.fn(),
|
moveHistoryIndexForward: jest.fn(),
|
||||||
|
submitReaction: jest.fn(),
|
||||||
addReaction: jest.fn(),
|
addReaction: jest.fn(),
|
||||||
removeReaction: jest.fn(),
|
removeReaction: jest.fn(),
|
||||||
clearDraftUploads: jest.fn(),
|
clearDraftUploads: jest.fn(),
|
||||||
@@ -728,13 +729,13 @@ describe('components/advanced_create_post', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('onSubmit test for addReaction message', async () => {
|
it('onSubmit test for addReaction message', async () => {
|
||||||
const addReaction = jest.fn();
|
const submitReaction = jest.fn();
|
||||||
|
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(
|
||||||
advancedCreatePost({
|
advancedCreatePost({
|
||||||
actions: {
|
actions: {
|
||||||
...baseProp.actions,
|
...baseProp.actions,
|
||||||
addReaction,
|
submitReaction,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -744,17 +745,17 @@ describe('components/advanced_create_post', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await (wrapper.instance() as AdvancedCreatePost).handleSubmit(submitEvent);
|
await (wrapper.instance() as AdvancedCreatePost).handleSubmit(submitEvent);
|
||||||
expect(addReaction).toHaveBeenCalledWith('a', 'smile');
|
expect(submitReaction).toHaveBeenCalledWith('a', '+', 'smile');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('onSubmit test for removeReaction message', () => {
|
it('onSubmit test for removeReaction message', async () => {
|
||||||
const removeReaction = jest.fn();
|
const submitReaction = jest.fn();
|
||||||
|
|
||||||
const wrapper = shallow(
|
const wrapper = shallow(
|
||||||
advancedCreatePost({
|
advancedCreatePost({
|
||||||
actions: {
|
actions: {
|
||||||
...baseProp.actions,
|
...baseProp.actions,
|
||||||
removeReaction,
|
submitReaction,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -763,9 +764,8 @@ describe('components/advanced_create_post', () => {
|
|||||||
message: '-:smile:',
|
message: '-:smile:',
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = wrapper.find('#create_post');
|
await (wrapper.instance() as AdvancedCreatePost).handleSubmit(submitEvent);
|
||||||
form.simulate('Submit', {preventDefault: jest.fn()});
|
expect(submitReaction).toHaveBeenCalledWith('a', '-', 'smile');
|
||||||
expect(removeReaction).toHaveBeenCalledWith('a', 'smile');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/*it('check for postError state on handlePostError callback', () => {
|
/*it('check for postError state on handlePostError callback', () => {
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ export type Props = {
|
|||||||
// func called for navigation through messages by Down arrow
|
// func called for navigation through messages by Down arrow
|
||||||
moveHistoryIndexForward: (index: string) => Promise<void>;
|
moveHistoryIndexForward: (index: string) => Promise<void>;
|
||||||
|
|
||||||
|
submitReaction: (postId: string, action: string, emojiName: string) => void;
|
||||||
|
|
||||||
// func called for adding a reaction
|
// func called for adding a reaction
|
||||||
addReaction: (postId: string, emojiName: string) => void;
|
addReaction: (postId: string, emojiName: string) => void;
|
||||||
|
|
||||||
@@ -799,10 +801,8 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
|||||||
const emojiName = isReaction[2];
|
const emojiName = isReaction[2];
|
||||||
const postId = this.props.latestReplyablePostId;
|
const postId = this.props.latestReplyablePostId;
|
||||||
|
|
||||||
if (postId && action === '+') {
|
if (postId) {
|
||||||
this.props.actions.addReaction(postId, emojiName);
|
this.props.actions.submitReaction(postId, action, emojiName);
|
||||||
} else if (postId && action === '-') {
|
|
||||||
this.props.actions.removeReaction(postId, emojiName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.actions.setDraft(StoragePrefixes.DRAFT + channelId, null, channelId);
|
this.props.actions.setDraft(StoragePrefixes.DRAFT + channelId, null, channelId);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import type {ActionResult, GetStateFunc, DispatchFunc} from 'mattermost-redux/ty
|
|||||||
|
|
||||||
import {executeCommand} from 'actions/command';
|
import {executeCommand} from 'actions/command';
|
||||||
import {runMessageWillBePostedHooks, runSlashCommandWillBePostedHooks} from 'actions/hooks';
|
import {runMessageWillBePostedHooks, runSlashCommandWillBePostedHooks} from 'actions/hooks';
|
||||||
import {addReaction, createPost, setEditingPost, emitShortcutReactToLastPostFrom} from 'actions/post_actions';
|
import {addReaction, createPost, setEditingPost, emitShortcutReactToLastPostFrom, submitReaction} from 'actions/post_actions';
|
||||||
import {actionOnGlobalItemsWithPrefix} from 'actions/storage';
|
import {actionOnGlobalItemsWithPrefix} from 'actions/storage';
|
||||||
import {scrollPostListToBottom} from 'actions/views/channel';
|
import {scrollPostListToBottom} from 'actions/views/channel';
|
||||||
import {removeDraft, updateDraft} from 'actions/views/drafts';
|
import {removeDraft, updateDraft} from 'actions/views/drafts';
|
||||||
@@ -153,6 +153,7 @@ type Actions = {
|
|||||||
addReaction: (postId: string, emojiName: string) => void;
|
addReaction: (postId: string, emojiName: string) => void;
|
||||||
onSubmitPost: (post: Post, fileInfos: FileInfo[]) => void;
|
onSubmitPost: (post: Post, fileInfos: FileInfo[]) => void;
|
||||||
removeReaction: (postId: string, emojiName: string) => void;
|
removeReaction: (postId: string, emojiName: string) => void;
|
||||||
|
submitReaction: (postId: string, action: string, emojiName: string) => void;
|
||||||
clearDraftUploads: () => void;
|
clearDraftUploads: () => void;
|
||||||
runMessageWillBePostedHooks: (originalPost: Post) => ActionResult;
|
runMessageWillBePostedHooks: (originalPost: Post) => ActionResult;
|
||||||
runSlashCommandWillBePostedHooks: (originalMessage: string, originalArgs: CommandArgs) => ActionResult;
|
runSlashCommandWillBePostedHooks: (originalMessage: string, originalArgs: CommandArgs) => ActionResult;
|
||||||
@@ -202,6 +203,7 @@ function mapDispatchToProps(dispatch: Dispatch) {
|
|||||||
onSubmitPost,
|
onSubmitPost,
|
||||||
moveHistoryIndexBack,
|
moveHistoryIndexBack,
|
||||||
moveHistoryIndexForward,
|
moveHistoryIndexForward,
|
||||||
|
submitReaction,
|
||||||
addReaction,
|
addReaction,
|
||||||
removeReaction,
|
removeReaction,
|
||||||
setDraft,
|
setDraft,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {ActionCreatorsMapObject, Dispatch} from 'redux';
|
|||||||
|
|
||||||
import type {Action} from 'mattermost-redux/types/actions';
|
import type {Action} from 'mattermost-redux/types/actions';
|
||||||
|
|
||||||
import {addReaction} from 'actions/post_actions';
|
import {toggleReaction} from 'actions/post_actions';
|
||||||
|
|
||||||
import PostReaction from './post_reaction';
|
import PostReaction from './post_reaction';
|
||||||
import type {Props} from './post_reaction';
|
import type {Props} from './post_reaction';
|
||||||
@@ -15,7 +15,7 @@ import type {Props} from './post_reaction';
|
|||||||
function mapDispatchToProps(dispatch: Dispatch) {
|
function mapDispatchToProps(dispatch: Dispatch) {
|
||||||
return {
|
return {
|
||||||
actions: bindActionCreators<ActionCreatorsMapObject<Action>, Props['actions']>({
|
actions: bindActionCreators<ActionCreatorsMapObject<Action>, Props['actions']>({
|
||||||
addReaction,
|
toggleReaction,
|
||||||
}, dispatch),
|
}, dispatch),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe('components/post_view/PostReaction', () => {
|
|||||||
showEmojiPicker: false,
|
showEmojiPicker: false,
|
||||||
toggleEmojiPicker: jest.fn(),
|
toggleEmojiPicker: jest.fn(),
|
||||||
actions: {
|
actions: {
|
||||||
addReaction: jest.fn(),
|
toggleReaction: jest.fn(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,13 +27,13 @@ describe('components/post_view/PostReaction', () => {
|
|||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should call addReaction and toggleEmojiPicker on handleAddEmoji', () => {
|
test('should call toggleReaction and toggleEmojiPicker on handleToggleEmoji', () => {
|
||||||
const wrapper = shallow(<PostReaction {...baseProps}/>);
|
const wrapper = shallow(<PostReaction {...baseProps}/>);
|
||||||
const instance = wrapper.instance() as PostReaction;
|
const instance = wrapper.instance() as PostReaction;
|
||||||
|
|
||||||
instance.handleAddEmoji({name: 'smile'} as Emoji);
|
instance.handleToggleEmoji({name: 'smile'} as Emoji);
|
||||||
expect(baseProps.actions.addReaction).toHaveBeenCalledTimes(1);
|
expect(baseProps.actions.toggleReaction).toHaveBeenCalledTimes(1);
|
||||||
expect(baseProps.actions.addReaction).toHaveBeenCalledWith('post_id_1', 'smile');
|
expect(baseProps.actions.toggleReaction).toHaveBeenCalledWith('post_id_1', 'smile');
|
||||||
expect(baseProps.toggleEmojiPicker).toHaveBeenCalledTimes(1);
|
expect(baseProps.toggleEmojiPicker).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export type Props = {
|
|||||||
showEmojiPicker: boolean;
|
showEmojiPicker: boolean;
|
||||||
toggleEmojiPicker: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
toggleEmojiPicker: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||||
actions: {
|
actions: {
|
||||||
addReaction: (postId: string, emojiName: string) => (dispatch: Dispatch) => {data: boolean};
|
toggleReaction: (postId: string, emojiName: string) => (dispatch: Dispatch) => {data: boolean};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,10 +45,10 @@ export default class PostReaction extends React.PureComponent<Props, State> {
|
|||||||
showEmojiPicker: false,
|
showEmojiPicker: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleAddEmoji = (emoji: Emoji): void => {
|
handleToggleEmoji = (emoji: Emoji): void => {
|
||||||
this.setState({showEmojiPicker: false});
|
this.setState({showEmojiPicker: false});
|
||||||
const emojiName = 'short_name' in emoji ? emoji.short_name : emoji.name;
|
const emojiName = 'short_name' in emoji ? emoji.short_name : emoji.name;
|
||||||
this.props.actions.addReaction(this.props.postId, emojiName);
|
this.props.actions.toggleReaction(this.props.postId, emojiName);
|
||||||
this.props.toggleEmojiPicker();
|
this.props.toggleEmojiPicker();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ export default class PostReaction extends React.PureComponent<Props, State> {
|
|||||||
show={showEmojiPicker}
|
show={showEmojiPicker}
|
||||||
target={this.props.getDotMenuRef}
|
target={this.props.getDotMenuRef}
|
||||||
onHide={this.props.toggleEmojiPicker}
|
onHide={this.props.toggleEmojiPicker}
|
||||||
onEmojiClick={this.handleAddEmoji}
|
onEmojiClick={this.handleToggleEmoji}
|
||||||
topOffset={TOP_OFFSET}
|
topOffset={TOP_OFFSET}
|
||||||
spaceRequiredAbove={spaceRequiredAbove}
|
spaceRequiredAbove={spaceRequiredAbove}
|
||||||
spaceRequiredBelow={spaceRequiredBelow}
|
spaceRequiredBelow={spaceRequiredBelow}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type {Emoji} from '@mattermost/types/emojis';
|
|||||||
|
|
||||||
import type {GenericAction} from 'mattermost-redux/types/actions';
|
import type {GenericAction} from 'mattermost-redux/types/actions';
|
||||||
|
|
||||||
import {addReaction} from 'actions/post_actions';
|
import {toggleReaction} from 'actions/post_actions';
|
||||||
import {getEmojiMap} from 'selectors/emojis';
|
import {getEmojiMap} from 'selectors/emojis';
|
||||||
import {getCurrentLocale} from 'selectors/i18n';
|
import {getCurrentLocale} from 'selectors/i18n';
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ import PostReaction from './post_recent_reactions';
|
|||||||
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
||||||
return {
|
return {
|
||||||
actions: bindActionCreators({
|
actions: bindActionCreators({
|
||||||
addReaction,
|
toggleReaction,
|
||||||
}, dispatch),
|
}, dispatch),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type Props = {
|
|||||||
size: number;
|
size: number;
|
||||||
defaultEmojis: Emoji[];
|
defaultEmojis: Emoji[];
|
||||||
actions: {
|
actions: {
|
||||||
addReaction: (postId: string, emojiName: string) => void;
|
toggleReaction: (postId: string, emojiName: string) => void;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,9 +41,9 @@ export default class PostRecentReactions extends React.PureComponent<Props, Stat
|
|||||||
size: 3,
|
size: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleAddEmoji = (emoji: Emoji): void => {
|
handleToggleEmoji = (emoji: Emoji): void => {
|
||||||
const emojiName = 'short_name' in emoji ? emoji.short_name : emoji.name;
|
const emojiName = 'short_name' in emoji ? emoji.short_name : emoji.name;
|
||||||
this.props.actions.addReaction(this.props.postId, emojiName);
|
this.props.actions.toggleReaction(this.props.postId, emojiName);
|
||||||
};
|
};
|
||||||
|
|
||||||
complementEmojis = (emojis: Emoji[]): (Emoji[]) => {
|
complementEmojis = (emojis: Emoji[]): (Emoji[]) => {
|
||||||
@@ -108,7 +108,7 @@ export default class PostRecentReactions extends React.PureComponent<Props, Stat
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<EmojiItem
|
<EmojiItem
|
||||||
emoji={emoji}
|
emoji={emoji}
|
||||||
onItemClick={this.handleAddEmoji}
|
onItemClick={this.handleToggleEmoji}
|
||||||
order={n}
|
order={n}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {getChannel} from 'mattermost-redux/selectors/entities/channels';
|
|||||||
import {canAddReactions} from 'mattermost-redux/selectors/entities/reactions';
|
import {canAddReactions} from 'mattermost-redux/selectors/entities/reactions';
|
||||||
import type {GenericAction} from 'mattermost-redux/types/actions';
|
import type {GenericAction} from 'mattermost-redux/types/actions';
|
||||||
|
|
||||||
import {addReaction} from 'actions/post_actions';
|
import {toggleReaction} from 'actions/post_actions';
|
||||||
|
|
||||||
import {makeGetUniqueReactionsToPost} from 'utils/post_utils';
|
import {makeGetUniqueReactionsToPost} from 'utils/post_utils';
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ function makeMapStateToProps() {
|
|||||||
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
||||||
return {
|
return {
|
||||||
actions: bindActionCreators({
|
actions: bindActionCreators({
|
||||||
addReaction,
|
toggleReaction,
|
||||||
}, dispatch),
|
}, dispatch),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ type Props = {
|
|||||||
/**
|
/**
|
||||||
* Function to add a reaction to the post
|
* Function to add a reaction to the post
|
||||||
*/
|
*/
|
||||||
addReaction: (postId: string, emojiName: string) => void;
|
toggleReaction: (postId: string, emojiName: string) => void;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ export default class ReactionList extends React.PureComponent<Props, State> {
|
|||||||
handleEmojiClick = (emoji: Emoji): void => {
|
handleEmojiClick = (emoji: Emoji): void => {
|
||||||
this.setState({showEmojiPicker: false});
|
this.setState({showEmojiPicker: false});
|
||||||
const emojiName = isSystemEmoji(emoji) ? emoji.short_names[0] : emoji.name;
|
const emojiName = isSystemEmoji(emoji) ? emoji.short_names[0] : emoji.name;
|
||||||
this.props.actions.addReaction(this.props.post.id, emojiName);
|
this.props.actions.toggleReaction(this.props.post.id, emojiName);
|
||||||
};
|
};
|
||||||
|
|
||||||
hideEmojiPicker = (): void => {
|
hideEmojiPicker = (): void => {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ describe('components/ReactionList', () => {
|
|||||||
const teamId = 'teamId';
|
const teamId = 'teamId';
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
addReaction: jest.fn(),
|
toggleReaction: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const baseProps = {
|
const baseProps = {
|
||||||
|
|||||||
@@ -1319,3 +1319,37 @@ describe('PostUtils.getUserOrGroupFromMentionName', () => {
|
|||||||
expect(result).toEqual(expected);
|
expect(result).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('makeGetIsReactionAlreadyAddedToPost', () => {
|
||||||
|
const currentUserId = 'current_user_id';
|
||||||
|
|
||||||
|
const baseState = {
|
||||||
|
entities: {
|
||||||
|
users: {
|
||||||
|
currentUserId,
|
||||||
|
},
|
||||||
|
posts: {
|
||||||
|
reactions: {
|
||||||
|
post_id_1: {
|
||||||
|
'current_user_id-smile': {
|
||||||
|
emoji_name: 'smile',
|
||||||
|
user_id: currentUserId,
|
||||||
|
post_id: 'post_id_1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
general: {
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
emojis: {},
|
||||||
|
}} as unknown as GlobalState;
|
||||||
|
|
||||||
|
test('should return true if the post has an emoji that the user has reacted to.', () => {
|
||||||
|
const getIsReactionAlreadyAddedToPost = PostUtils.makeGetIsReactionAlreadyAddedToPost();
|
||||||
|
|
||||||
|
expect(getIsReactionAlreadyAddedToPost(baseState, 'post_id_1', 'sad')).toBeFalsy();
|
||||||
|
expect(getIsReactionAlreadyAddedToPost(baseState, 'post_id_1', 'smile')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -710,6 +710,24 @@ export function makeGetUniqueReactionsToPost(): (state: GlobalState, postId: Pos
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function makeGetIsReactionAlreadyAddedToPost(): (state: GlobalState, postId: Post['id'], emojiName: string) => boolean {
|
||||||
|
const getUniqueReactionsToPost = makeGetUniqueReactionsToPost();
|
||||||
|
|
||||||
|
return createSelector(
|
||||||
|
'makeGetIsReactionAlreadyAddedToPost',
|
||||||
|
(state: GlobalState, postId: string) => getUniqueReactionsToPost(state, postId),
|
||||||
|
getCurrentUserId,
|
||||||
|
(state: GlobalState, postId: string, emojiName: string) => emojiName,
|
||||||
|
(reactions, currentUserId, emojiName) => {
|
||||||
|
const reactionsForPost = reactions || {};
|
||||||
|
|
||||||
|
const isReactionAlreadyAddedToPost = Object.values(reactionsForPost).some((reaction) => reaction.user_id === currentUserId && reaction.emoji_name === emojiName);
|
||||||
|
|
||||||
|
return isReactionAlreadyAddedToPost;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getMentionDetails(usersByUsername: Record<string, UserProfile | Group>, mentionName: string): UserProfile | Group | undefined {
|
export function getMentionDetails(usersByUsername: Record<string, UserProfile | Group>, mentionName: string): UserProfile | Group | undefined {
|
||||||
let mentionNameToLowerCase = mentionName.toLowerCase();
|
let mentionNameToLowerCase = mentionName.toLowerCase();
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user