Commit 0e8f4ae8 authored by Simon Knox's avatar Simon Knox

Merge branch 'Give-immediate-feedback-when-emoji-reacting-to-a-comment' into 'master'

Give immediate feedback when awarding an issuable

See merge request gitlab-org/gitlab!75808
parents 8b7b169c 84deccab
......@@ -33,20 +33,51 @@ export const fetchAwards = async ({ commit, dispatch, state }, page = '1') => {
}
};
/**
* Creates an intermediary award, used for display
* until the real award is loaded from the backend.
*/
const newOptimisticAward = (name, state) => {
const freeId = Math.min(...state.awards.map((a) => a.id), Number.MAX_SAFE_INTEGER) - 1;
return {
id: freeId,
name,
user: {
id: window.gon.current_user_id,
name: window.gon.current_user_fullname,
username: window.gon.current_username,
},
};
};
export const toggleAward = async ({ commit, state }, name) => {
const award = state.awards.find((a) => a.name === name && a.user.id === state.currentUserId);
try {
if (award) {
await axios.delete(joinPaths(gon.relative_url_root || '', `${state.path}/${award.id}`));
commit(REMOVE_AWARD, award.id);
await axios
.delete(joinPaths(gon.relative_url_root || '', `${state.path}/${award.id}`))
.catch((err) => {
commit(ADD_NEW_AWARD, award);
throw err;
});
showToast(__('Award removed'));
} else {
const { data } = await axios.post(joinPaths(gon.relative_url_root || '', state.path), {
name,
});
const optimisticAward = newOptimisticAward(name, state);
commit(ADD_NEW_AWARD, optimisticAward);
const { data } = await axios
.post(joinPaths(gon.relative_url_root || '', state.path), {
name,
})
.finally(() => {
commit(REMOVE_AWARD, optimisticAward.id);
});
commit(ADD_NEW_AWARD, data);
......
......@@ -27,6 +27,7 @@ RSpec.describe 'Merge request > User awards emoji', :js do
it 'removes award from merge request' do
first('[data-testid="award-button"]').click
expect(first('[data-testid="award-button"]')).to have_content '1'
find('[data-testid="award-button"].selected').click
expect(first('[data-testid="award-button"]')).to have_content '0'
......
......@@ -87,6 +87,26 @@ describe('Awards app actions', () => {
describe('toggleAward', () => {
let mock;
const optimisticAwardId = Number.MAX_SAFE_INTEGER - 1;
const makeOptimisticAddMutation = (
id = optimisticAwardId,
name = null,
userId = window.gon.current_user_id,
) => ({
type: 'ADD_NEW_AWARD',
payload: {
id,
name,
user: {
id: userId,
},
},
});
const makeOptimisticRemoveMutation = (id = optimisticAwardId) => ({
type: 'REMOVE_AWARD',
payload: id,
});
beforeEach(() => {
mock = new MockAdapter(axios);
});
......@@ -110,8 +130,10 @@ describe('Awards app actions', () => {
mock.onPost(`${relativeRootUrl || ''}/awards`).reply(200, { id: 1 });
});
it('commits ADD_NEW_AWARD', async () => {
it('adds an optimistic award, removes it, and then commits ADD_NEW_AWARD', async () => {
testAction(actions.toggleAward, null, { path: '/awards', awards: [] }, [
makeOptimisticAddMutation(),
makeOptimisticRemoveMutation(),
{ type: 'ADD_NEW_AWARD', payload: { id: 1 } },
]);
});
......@@ -127,7 +149,7 @@ describe('Awards app actions', () => {
actions.toggleAward,
null,
{ path: '/awards', awards: [] },
[],
[makeOptimisticAddMutation(), makeOptimisticRemoveMutation()],
[],
() => {
expect(Sentry.captureException).toHaveBeenCalled();
......@@ -137,7 +159,7 @@ describe('Awards app actions', () => {
});
});
describe('removing a award', () => {
describe('removing an award', () => {
const mockData = { id: 1, name: 'thumbsup', user: { id: 1 } };
describe('success', () => {
......@@ -160,6 +182,9 @@ describe('Awards app actions', () => {
});
describe('error', () => {
const currentUserId = 1;
const name = 'thumbsup';
beforeEach(() => {
mock.onDelete(`${relativeRootUrl || ''}/awards/1`).reply(500);
});
......@@ -167,13 +192,13 @@ describe('Awards app actions', () => {
it('calls Sentry.captureException', async () => {
await testAction(
actions.toggleAward,
'thumbsup',
name,
{
path: '/awards',
currentUserId: 1,
currentUserId,
awards: [mockData],
},
[],
[makeOptimisticRemoveMutation(1), makeOptimisticAddMutation(1, name, currentUserId)],
[],
() => {
expect(Sentry.captureException).toHaveBeenCalled();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment