From d5d8c4877e2ee51ee619f04350c9616b9fd3276b Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 27 Sep 2020 17:24:55 -0500 Subject: [PATCH] Interactions: optimistic Favourite and EmojiReact actions, fixes #104 --- app/soapbox/reducers/statuses.js | 28 +++++++++++++++---- .../utils/__tests__/emoji_reacts-test.js | 26 +++++++++++++++++ app/soapbox/utils/emoji_reacts.js | 17 +++++++++++ 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/app/soapbox/reducers/statuses.js b/app/soapbox/reducers/statuses.js index 8cccd4a09..b7f09ef22 100644 --- a/app/soapbox/reducers/statuses.js +++ b/app/soapbox/reducers/statuses.js @@ -2,6 +2,7 @@ import { REBLOG_REQUEST, REBLOG_FAIL, FAVOURITE_REQUEST, + UNFAVOURITE_REQUEST, FAVOURITE_FAIL, } from '../actions/interactions'; import { @@ -12,11 +13,12 @@ import { } from '../actions/statuses'; import { EMOJI_REACT_REQUEST, + UNEMOJI_REACT_REQUEST, } from '../actions/emoji_reacts'; import { TIMELINE_DELETE } from '../actions/timelines'; import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; import { Map as ImmutableMap, fromJS } from 'immutable'; -import { simulateEmojiReact } from 'soapbox/utils/emoji_reacts'; +import { simulateEmojiReact, simulateUnEmojiReact } from 'soapbox/utils/emoji_reacts'; const importStatus = (state, status) => state.set(status.id, fromJS(status)); @@ -40,11 +42,27 @@ export default function statuses(state = initialState, action) { case STATUSES_IMPORT: return importStatuses(state, action.statuses); case FAVOURITE_REQUEST: - return state.setIn([action.status.get('id'), 'favourited'], true); + return state.update(action.status.get('id'), status => + status + .set('favourited', true) + .update('favourites_count', count => count + 1)); + case UNFAVOURITE_REQUEST: + return state.update(action.status.get('id'), status => + status + .set('favourited', false) + .update('favourites_count', count => Math.max(0, count - 1))); case EMOJI_REACT_REQUEST: - const path = [action.status.get('id'), 'pleroma', 'emoji_reactions']; - const emojiReacts = state.getIn(path); - return state.setIn(path, simulateEmojiReact(emojiReacts, action.emoji)); + return state + .updateIn( + [action.status.get('id'), 'pleroma', 'emoji_reactions'], + emojiReacts => simulateEmojiReact(emojiReacts, action.emoji) + ); + case UNEMOJI_REACT_REQUEST: + return state + .updateIn( + [action.status.get('id'), 'pleroma', 'emoji_reactions'], + emojiReacts => simulateUnEmojiReact(emojiReacts, action.emoji) + ); case FAVOURITE_FAIL: return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'favourited'], false); case REBLOG_REQUEST: diff --git a/app/soapbox/utils/__tests__/emoji_reacts-test.js b/app/soapbox/utils/__tests__/emoji_reacts-test.js index fcce0a11b..4663304b2 100644 --- a/app/soapbox/utils/__tests__/emoji_reacts-test.js +++ b/app/soapbox/utils/__tests__/emoji_reacts-test.js @@ -6,6 +6,7 @@ import { reduceEmoji, getReactForStatus, simulateEmojiReact, + simulateUnEmojiReact, } from '../emoji_reacts'; import { fromJS } from 'immutable'; @@ -205,3 +206,28 @@ describe('simulateEmojiReact', () => { ])); }); }); + +describe('simulateUnEmojiReact', () => { + it('removes the emoji from the list', () => { + const emojiReacts = fromJS([ + { 'count': 2, 'me': false, 'name': '👍' }, + { 'count': 3, 'me': true, 'name': '❤' }, + ]); + expect(simulateUnEmojiReact(emojiReacts, '❤')).toEqual(fromJS([ + { 'count': 2, 'me': false, 'name': '👍' }, + { 'count': 2, 'me': false, 'name': '❤' }, + ])); + }); + + it('removes the emoji if it\'s the last one in the list', () => { + const emojiReacts = fromJS([ + { 'count': 2, 'me': false, 'name': '👍' }, + { 'count': 2, 'me': false, 'name': '❤' }, + { 'count': 1, 'me': true, 'name': '😯' }, + ]); + expect(simulateUnEmojiReact(emojiReacts, '😯')).toEqual(fromJS([ + { 'count': 2, 'me': false, 'name': '👍' }, + { 'count': 2, 'me': false, 'name': '❤' }, + ])); + }); +}); diff --git a/app/soapbox/utils/emoji_reacts.js b/app/soapbox/utils/emoji_reacts.js index 2cb1b3fc3..39c194101 100644 --- a/app/soapbox/utils/emoji_reacts.js +++ b/app/soapbox/utils/emoji_reacts.js @@ -100,3 +100,20 @@ export const simulateEmojiReact = (emojiReacts, emoji) => { })); } }; + +export const simulateUnEmojiReact = (emojiReacts, emoji) => { + const idx = emojiReacts.findIndex(e => + e.get('name') === emoji && e.get('me') === true); + + if (idx > -1) { + const emojiReact = emojiReacts.get(idx); + const newCount = emojiReact.get('count') - 1; + if (newCount < 1) return emojiReacts.delete(idx); + return emojiReacts.set(idx, emojiReact.merge({ + count: emojiReact.get('count') - 1, + me: false, + })); + } else { + return emojiReacts; + } +};