mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[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>
This commit is contained in:
parent
2184876c77
commit
34ce0d00d4
@ -12,10 +12,12 @@ import * as Actions from 'actions/post_actions';
|
||||
|
||||
import mockStore from 'tests/test_store';
|
||||
import {Constants, ActionTypes, RHSStates} from 'utils/constants';
|
||||
import * as PostUtils from 'utils/post_utils';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
jest.mock('mattermost-redux/actions/posts', () => ({
|
||||
removeReaction: (...args: any[]) => ({type: 'MOCK_REMOVE_REACTION', args}),
|
||||
addReaction: (...args: any[]) => ({type: 'MOCK_ADD_REACTION', args}),
|
||||
createPost: (...args: any[]) => ({type: 'MOCK_CREATE_POST', args}),
|
||||
createPostImmediately: (...args: any[]) => ({type: 'MOCK_CREATE_POST_IMMEDIATELY', args}),
|
||||
@ -48,6 +50,8 @@ jest.mock('utils/user_agent', () => ({
|
||||
isDesktopApp: jest.fn().mockReturnValue(false),
|
||||
}));
|
||||
|
||||
const mockMakeGetIsReactionAlreadyAddedToPost = jest.spyOn(PostUtils, 'makeGetIsReactionAlreadyAddedToPost');
|
||||
|
||||
const POST_CREATED_TIME = Date.now();
|
||||
|
||||
// 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 () => {
|
||||
const testStore = mockStore(initialState);
|
||||
|
||||
|
@ -34,6 +34,7 @@ import {
|
||||
StoragePrefixes,
|
||||
} from 'utils/constants';
|
||||
import {matchEmoticons} from 'utils/emoticons';
|
||||
import {makeGetIsReactionAlreadyAddedToPost} from 'utils/post_utils';
|
||||
import * as UserAgent from 'utils/user_agent';
|
||||
|
||||
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) {
|
||||
return (dispatch: DispatchFunc) => {
|
||||
dispatch(PostActions.addReaction(postId, emojiName));
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {
|
||||
removeReaction,
|
||||
addMessageIntoHistory,
|
||||
moveHistoryIndexBack,
|
||||
} from 'mattermost-redux/actions/posts';
|
||||
@ -17,7 +16,6 @@ import {
|
||||
updateCommentDraft,
|
||||
makeOnMoveHistoryIndex,
|
||||
submitPost,
|
||||
submitReaction,
|
||||
submitCommand,
|
||||
makeOnSubmit,
|
||||
makeOnEditLatestPost,
|
||||
@ -63,6 +61,7 @@ jest.mock('actions/hooks', () => ({
|
||||
}));
|
||||
|
||||
jest.mock('actions/post_actions', () => ({
|
||||
submitReaction: (...args) => ({type: 'MOCK_SUBMIT_REACTION', args}),
|
||||
addReaction: (...args) => ({type: 'MOCK_ADD_REACTION', args}),
|
||||
createPost: jest.fn(() => ({type: 'MOCK_CREATE_POST'})),
|
||||
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', () => {
|
||||
const args = {
|
||||
channel_id: channelId,
|
||||
@ -400,7 +380,7 @@ describe('rhs view actions', () => {
|
||||
}));
|
||||
|
||||
const testStore = mockStore(initialState);
|
||||
testStore.dispatch(submitReaction(latestPostId, '+', 'smile'));
|
||||
testStore.dispatch(PostActions.submitReaction(latestPostId, '+', 'smile'));
|
||||
|
||||
expect(store.getActions()).toEqual(
|
||||
expect.arrayContaining(testStore.getActions()),
|
||||
|
@ -4,7 +4,6 @@
|
||||
import type {Post} from '@mattermost/types/posts';
|
||||
|
||||
import {
|
||||
removeReaction,
|
||||
addMessageIntoHistory,
|
||||
moveHistoryIndexBack,
|
||||
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) {
|
||||
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
|
||||
const state = getState();
|
||||
@ -170,7 +158,7 @@ export function makeOnSubmit(channelId: string, rootId: string, latestPostId: st
|
||||
const emojiMap = new EmojiMap(emojis);
|
||||
|
||||
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) {
|
||||
try {
|
||||
await dispatch(submitCommand(channelId, rootId, draft));
|
||||
|
@ -76,6 +76,7 @@ const baseProp: Props = {
|
||||
addMessageIntoHistory: jest.fn(),
|
||||
moveHistoryIndexBack: jest.fn(),
|
||||
moveHistoryIndexForward: jest.fn(),
|
||||
submitReaction: jest.fn(),
|
||||
addReaction: jest.fn(),
|
||||
removeReaction: jest.fn(),
|
||||
clearDraftUploads: jest.fn(),
|
||||
@ -728,13 +729,13 @@ describe('components/advanced_create_post', () => {
|
||||
});
|
||||
|
||||
it('onSubmit test for addReaction message', async () => {
|
||||
const addReaction = jest.fn();
|
||||
const submitReaction = jest.fn();
|
||||
|
||||
const wrapper = shallow(
|
||||
advancedCreatePost({
|
||||
actions: {
|
||||
...baseProp.actions,
|
||||
addReaction,
|
||||
submitReaction,
|
||||
},
|
||||
}),
|
||||
);
|
||||
@ -744,17 +745,17 @@ describe('components/advanced_create_post', () => {
|
||||
});
|
||||
|
||||
await (wrapper.instance() as AdvancedCreatePost).handleSubmit(submitEvent);
|
||||
expect(addReaction).toHaveBeenCalledWith('a', 'smile');
|
||||
expect(submitReaction).toHaveBeenCalledWith('a', '+', 'smile');
|
||||
});
|
||||
|
||||
it('onSubmit test for removeReaction message', () => {
|
||||
const removeReaction = jest.fn();
|
||||
it('onSubmit test for removeReaction message', async () => {
|
||||
const submitReaction = jest.fn();
|
||||
|
||||
const wrapper = shallow(
|
||||
advancedCreatePost({
|
||||
actions: {
|
||||
...baseProp.actions,
|
||||
removeReaction,
|
||||
submitReaction,
|
||||
},
|
||||
}),
|
||||
);
|
||||
@ -763,9 +764,8 @@ describe('components/advanced_create_post', () => {
|
||||
message: '-:smile:',
|
||||
});
|
||||
|
||||
const form = wrapper.find('#create_post');
|
||||
form.simulate('Submit', {preventDefault: jest.fn()});
|
||||
expect(removeReaction).toHaveBeenCalledWith('a', 'smile');
|
||||
await (wrapper.instance() as AdvancedCreatePost).handleSubmit(submitEvent);
|
||||
expect(submitReaction).toHaveBeenCalledWith('a', '-', 'smile');
|
||||
});
|
||||
|
||||
/*it('check for postError state on handlePostError callback', () => {
|
||||
|
@ -174,6 +174,8 @@ export type Props = {
|
||||
// func called for navigation through messages by Down arrow
|
||||
moveHistoryIndexForward: (index: string) => Promise<void>;
|
||||
|
||||
submitReaction: (postId: string, action: string, emojiName: string) => void;
|
||||
|
||||
// func called for adding a reaction
|
||||
addReaction: (postId: string, emojiName: string) => void;
|
||||
|
||||
@ -799,10 +801,8 @@ class AdvancedCreatePost extends React.PureComponent<Props, State> {
|
||||
const emojiName = isReaction[2];
|
||||
const postId = this.props.latestReplyablePostId;
|
||||
|
||||
if (postId && action === '+') {
|
||||
this.props.actions.addReaction(postId, emojiName);
|
||||
} else if (postId && action === '-') {
|
||||
this.props.actions.removeReaction(postId, emojiName);
|
||||
if (postId) {
|
||||
this.props.actions.submitReaction(postId, action, emojiName);
|
||||
}
|
||||
|
||||
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 {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 {scrollPostListToBottom} from 'actions/views/channel';
|
||||
import {removeDraft, updateDraft} from 'actions/views/drafts';
|
||||
@ -153,6 +153,7 @@ type Actions = {
|
||||
addReaction: (postId: string, emojiName: string) => void;
|
||||
onSubmitPost: (post: Post, fileInfos: FileInfo[]) => void;
|
||||
removeReaction: (postId: string, emojiName: string) => void;
|
||||
submitReaction: (postId: string, action: string, emojiName: string) => void;
|
||||
clearDraftUploads: () => void;
|
||||
runMessageWillBePostedHooks: (originalPost: Post) => ActionResult;
|
||||
runSlashCommandWillBePostedHooks: (originalMessage: string, originalArgs: CommandArgs) => ActionResult;
|
||||
@ -202,6 +203,7 @@ function mapDispatchToProps(dispatch: Dispatch) {
|
||||
onSubmitPost,
|
||||
moveHistoryIndexBack,
|
||||
moveHistoryIndexForward,
|
||||
submitReaction,
|
||||
addReaction,
|
||||
removeReaction,
|
||||
setDraft,
|
||||
|
@ -7,7 +7,7 @@ import type {ActionCreatorsMapObject, Dispatch} from 'redux';
|
||||
|
||||
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 type {Props} from './post_reaction';
|
||||
@ -15,7 +15,7 @@ import type {Props} from './post_reaction';
|
||||
function mapDispatchToProps(dispatch: Dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators<ActionCreatorsMapObject<Action>, Props['actions']>({
|
||||
addReaction,
|
||||
toggleReaction,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ describe('components/post_view/PostReaction', () => {
|
||||
showEmojiPicker: false,
|
||||
toggleEmojiPicker: jest.fn(),
|
||||
actions: {
|
||||
addReaction: jest.fn(),
|
||||
toggleReaction: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
@ -27,13 +27,13 @@ describe('components/post_view/PostReaction', () => {
|
||||
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 instance = wrapper.instance() as PostReaction;
|
||||
|
||||
instance.handleAddEmoji({name: 'smile'} as Emoji);
|
||||
expect(baseProps.actions.addReaction).toHaveBeenCalledTimes(1);
|
||||
expect(baseProps.actions.addReaction).toHaveBeenCalledWith('post_id_1', 'smile');
|
||||
instance.handleToggleEmoji({name: 'smile'} as Emoji);
|
||||
expect(baseProps.actions.toggleReaction).toHaveBeenCalledTimes(1);
|
||||
expect(baseProps.actions.toggleReaction).toHaveBeenCalledWith('post_id_1', 'smile');
|
||||
expect(baseProps.toggleEmojiPicker).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
@ -30,7 +30,7 @@ export type Props = {
|
||||
showEmojiPicker: boolean;
|
||||
toggleEmojiPicker: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
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,
|
||||
};
|
||||
|
||||
handleAddEmoji = (emoji: Emoji): void => {
|
||||
handleToggleEmoji = (emoji: Emoji): void => {
|
||||
this.setState({showEmojiPicker: false});
|
||||
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();
|
||||
};
|
||||
|
||||
@ -79,7 +79,7 @@ export default class PostReaction extends React.PureComponent<Props, State> {
|
||||
show={showEmojiPicker}
|
||||
target={this.props.getDotMenuRef}
|
||||
onHide={this.props.toggleEmojiPicker}
|
||||
onEmojiClick={this.handleAddEmoji}
|
||||
onEmojiClick={this.handleToggleEmoji}
|
||||
topOffset={TOP_OFFSET}
|
||||
spaceRequiredAbove={spaceRequiredAbove}
|
||||
spaceRequiredBelow={spaceRequiredBelow}
|
||||
|
@ -9,7 +9,7 @@ import type {Emoji} from '@mattermost/types/emojis';
|
||||
|
||||
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 {getCurrentLocale} from 'selectors/i18n';
|
||||
|
||||
@ -20,7 +20,7 @@ import PostReaction from './post_recent_reactions';
|
||||
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
addReaction,
|
||||
toggleReaction,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ type Props = {
|
||||
size: number;
|
||||
defaultEmojis: Emoji[];
|
||||
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,
|
||||
};
|
||||
|
||||
handleAddEmoji = (emoji: Emoji): void => {
|
||||
handleToggleEmoji = (emoji: Emoji): void => {
|
||||
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[]) => {
|
||||
@ -108,7 +108,7 @@ export default class PostRecentReactions extends React.PureComponent<Props, Stat
|
||||
<React.Fragment>
|
||||
<EmojiItem
|
||||
emoji={emoji}
|
||||
onItemClick={this.handleAddEmoji}
|
||||
onItemClick={this.handleToggleEmoji}
|
||||
order={n}
|
||||
/>
|
||||
</React.Fragment>
|
||||
|
@ -11,7 +11,7 @@ import {getChannel} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {canAddReactions} from 'mattermost-redux/selectors/entities/reactions';
|
||||
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';
|
||||
|
||||
@ -43,7 +43,7 @@ function makeMapStateToProps() {
|
||||
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
addReaction,
|
||||
toggleReaction,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ type Props = {
|
||||
/**
|
||||
* 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 => {
|
||||
this.setState({showEmojiPicker: false});
|
||||
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 => {
|
||||
|
@ -27,7 +27,7 @@ describe('components/ReactionList', () => {
|
||||
const teamId = 'teamId';
|
||||
|
||||
const actions = {
|
||||
addReaction: jest.fn(),
|
||||
toggleReaction: jest.fn(),
|
||||
};
|
||||
|
||||
const baseProps = {
|
||||
|
@ -1240,10 +1240,10 @@ describe('PostUtils.isWithinCodeBlock', () => {
|
||||
|
||||
it('should handle whitespace within and around code blocks', () => {
|
||||
const [caretPosition, message] = getCaretAndMsg(`
|
||||
|${TRIPLE_BACKTICKS}
|
||||
| Test text asd 1
|
||||
| ${CARET_MARKER}
|
||||
|${TRIPLE_BACKTICKS}
|
||||
|${TRIPLE_BACKTICKS}
|
||||
| Test text asd 1
|
||||
| ${CARET_MARKER}
|
||||
|${TRIPLE_BACKTICKS}
|
||||
`);
|
||||
expect(PostUtils.isWithinCodeBlock(message, caretPosition)).toBe(true);
|
||||
});
|
||||
@ -1319,3 +1319,37 @@ describe('PostUtils.getUserOrGroupFromMentionName', () => {
|
||||
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 {
|
||||
let mentionNameToLowerCase = mentionName.toLowerCase();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user