pl-fe: Fix reactions sorting, fix remote custom reactions

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak
2024-09-11 15:56:42 +02:00
parent 584cde8c40
commit 9f122d6828
4 changed files with 2 additions and 176 deletions

View File

@ -1,21 +0,0 @@
import { rootState } from 'pl-fe/jest/test-helpers';
import { RootState } from 'pl-fe/store';
import { getPlFeConfig } from './pl-fe';
const ASCII_HEART = '❤'; // '\u2764\uFE0F'
const RED_HEART_RGI = '❤️'; // '\u2764'
describe('getPlFeConfig()', () => {
it('returns RGI heart on Pleroma > 2.3', () => {
const state = rootState.setIn(['instance', 'version'], '2.7.2 (compatible; Pleroma 2.3.0)') as RootState;
expect(getPlFeConfig(state).allowedEmoji.includes(RED_HEART_RGI)).toBe(true);
expect(getPlFeConfig(state).allowedEmoji.includes(ASCII_HEART)).toBe(false);
});
it('returns an ASCII heart on Pleroma < 2.3', () => {
const state = rootState.setIn(['instance', 'version'], '2.7.2 (compatible; Pleroma 2.0.0)') as RootState;
expect(getPlFeConfig(state).allowedEmoji.includes(ASCII_HEART)).toBe(true);
expect(getPlFeConfig(state).allowedEmoji.includes(RED_HEART_RGI)).toBe(false);
});
});

View File

@ -7,7 +7,6 @@ import { emojiReact, unEmojiReact } from 'pl-fe/actions/emoji-reacts';
import EmojiPickerDropdown from 'pl-fe/features/emoji/containers/emoji-picker-dropdown-container';
import unicodeMapping from 'pl-fe/features/emoji/mapping';
import { useAppDispatch, useSettings } from 'pl-fe/hooks';
import { sortEmoji } from 'pl-fe/utils/emoji-reacts';
import AnimatedNumber from './animated-number';
import { Emoji, HStack, Icon, Text } from './ui';
@ -43,7 +42,7 @@ const StatusReaction: React.FC<IStatusReaction> = ({ reaction, status, obfuscate
if (reaction.me) {
dispatch(unEmojiReact(status, reaction.name));
} else {
dispatch(emojiReact(status, reaction.name));
dispatch(emojiReact(status, reaction.name, reaction.url));
}
};
@ -88,7 +87,7 @@ const StatusReactionsBar: React.FC<IStatusReactionsBar> = ({ status, collapsed }
if ((demetricator || status.emoji_reactions.length === 0) && collapsed) return null;
const sortedReactions = sortEmoji(status.emoji_reactions);
const sortedReactions = status.emoji_reactions.toSorted((a, b) => (b.count || 0) - (a.count || 0));
return (
<HStack className='pt-2' space={2} wrap>

View File

@ -1,159 +1,11 @@
import { List as ImmutableList, fromJS } from 'immutable';
import { emojiReactionSchema } from 'pl-api';
import { normalizeStatus } from 'pl-fe/normalizers';
import {
sortEmoji,
mergeEmojiFavourites,
reduceEmoji,
getReactForStatus,
simulateEmojiReact,
simulateUnEmojiReact,
} from './emoji-reacts';
const ALLOWED_EMOJI = ImmutableList([
'👍',
'❤',
'😂',
'😯',
'😢',
'😡',
]);
describe('sortEmoji', () => {
describe('with an unsorted list of emoji', () => {
const emojiReacts = ImmutableList([
{ 'count': 7, 'me': true, 'name': '😃' },
{ 'count': 7, 'me': true, 'name': '😯' },
{ 'count': 3, 'me': true, 'name': '😢' },
{ 'count': 1, 'me': true, 'name': '😡' },
{ 'count': 20, 'me': true, 'name': '👍' },
{ 'count': 7, 'me': true, 'name': '😂' },
{ 'count': 15, 'me': true, 'name': '❤' },
].map((react) => emojiReactionSchema.parse(react)));
it('sorts the emoji by count', () => {
expect(sortEmoji(emojiReacts, ALLOWED_EMOJI)).toEqual(fromJS([
{ 'count': 20, 'me': true, 'name': '👍' },
{ 'count': 15, 'me': true, 'name': '❤' },
{ 'count': 7, 'me': true, 'name': '😯' },
{ 'count': 7, 'me': true, 'name': '😂' },
{ 'count': 7, 'me': true, 'name': '😃' },
{ 'count': 3, 'me': true, 'name': '😢' },
{ 'count': 1, 'me': true, 'name': '😡' },
]));
});
});
});
describe('mergeEmojiFavourites', () => {
const favouritesCount = 12;
const favourited = true;
describe('with existing 👍 reacts', () => {
const emojiReacts = ImmutableList([
{ 'count': 20, 'me': false, 'name': '👍', 'url': undefined },
{ 'count': 15, 'me': false, 'name': '❤', 'url': undefined },
{ 'count': 7, 'me': false, 'name': '😯', 'url': undefined },
].map((react) => emojiReactionSchema.parse(react)));
it('combines 👍 reacts with favourites', () => {
expect(mergeEmojiFavourites(emojiReacts, favouritesCount, favourited)).toEqual(fromJS([
{ 'count': 32, 'me': true, 'name': '👍', 'url': undefined },
{ 'count': 15, 'me': false, 'name': '❤', 'url': undefined },
{ 'count': 7, 'me': false, 'name': '😯', 'url': undefined },
]));
});
});
describe('without existing 👍 reacts', () => {
const emojiReacts = ImmutableList([
{ 'count': 15, 'me': false, 'name': '❤' },
{ 'count': 7, 'me': false, 'name': '😯' },
].map((react) => emojiReactionSchema.parse(react)));
it('adds 👍 reacts to the map equaling favourite count', () => {
expect(mergeEmojiFavourites(emojiReacts, favouritesCount, favourited)).toEqual(fromJS([
{ 'count': 15, 'me': false, 'name': '❤' },
{ 'count': 7, 'me': false, 'name': '😯' },
{ 'count': 12, 'me': true, 'name': '👍' },
]));
});
it('does not add 👍 reacts when there are no favourites', () => {
expect(mergeEmojiFavourites(emojiReacts, 0, false)).toEqual(fromJS([
{ 'count': 15, 'me': false, 'name': '❤' },
{ 'count': 7, 'me': false, 'name': '😯' },
]));
});
});
});
describe('reduceEmoji', () => {
describe('with a clusterfuck of emoji', () => {
const emojiReacts = ImmutableList([
{ 'count': 1, 'me': false, 'name': '😡' },
{ 'count': 1, 'me': true, 'name': '🔪' },
{ 'count': 7, 'me': true, 'name': '😯' },
{ 'count': 3, 'me': false, 'name': '😢' },
{ 'count': 1, 'me': true, 'name': '🌵' },
{ 'count': 20, 'me': true, 'name': '👍' },
{ 'count': 7, 'me': false, 'name': '😂' },
{ 'count': 15, 'me': true, 'name': '❤' },
{ 'count': 1, 'me': false, 'name': '👀' },
{ 'count': 1, 'me': false, 'name': '🍩' },
].map((react) => emojiReactionSchema.parse(react)));
it('sorts, filters, and combines emoji and favourites', () => {
expect(reduceEmoji(emojiReacts, 7, true, ALLOWED_EMOJI)).toEqual(fromJS([
{ 'count': 27, 'me': true, 'name': '👍' },
{ 'count': 15, 'me': true, 'name': '❤' },
{ 'count': 7, 'me': true, 'name': '😯' },
{ 'count': 7, 'me': false, 'name': '😂' },
{ 'count': 3, 'me': false, 'name': '😢' },
{ 'count': 1, 'me': false, 'name': '😡' },
{ 'count': 1, 'me': true, 'name': '🔪' },
{ 'count': 1, 'me': true, 'name': '🌵' },
{ 'count': 1, 'me': false, 'name': '👀' },
{ 'count': 1, 'me': false, 'name': '🍩' },
]));
});
});
});
describe('getReactForStatus', () => {
it('returns a single owned react (including favourite) for the status', () => {
const status = normalizeStatus(fromJS({
favourited: false,
pleroma: {
emoji_reactions: [
{ 'count': 20, 'me': false, 'name': '👍' },
{ 'count': 15, 'me': true, 'name': '❤' },
{ 'count': 7, 'me': true, 'name': '😯' },
{ 'count': 7, 'me': false, 'name': '😂' },
],
},
}));
expect(getReactForStatus(status, ALLOWED_EMOJI)?.name).toEqual('❤');
});
it('returns a thumbs-up for a favourite', () => {
const status = normalizeStatus(fromJS({ favourites_count: 1, favourited: true }));
expect(getReactForStatus(status)?.name).toEqual('👍');
});
it('returns undefined when a status has no reacts (or favourites)', () => {
const status = normalizeStatus(fromJS({}));
expect(getReactForStatus(status)).toEqual(undefined);
});
it('returns undefined when a status has no valid reacts (or favourites)', () => {
const status = normalizeStatus(fromJS([
{ 'count': 1, 'me': true, 'name': '🔪' },
{ 'count': 1, 'me': true, 'name': '🌵' },
{ 'count': 1, 'me': false, 'name': '👀' },
{ 'count': 1, 'me': false, 'name': '🍩' },
]));
expect(getReactForStatus(status)).toEqual(undefined);
});
});
describe('simulateEmojiReact', () => {
it('adds the emoji to the list', () => {
const emojiReacts = ImmutableList([

View File

@ -1,8 +1,5 @@
import { emojiReactionSchema, type EmojiReaction } from 'pl-api';
const sortEmoji = (emojiReacts: Array<EmojiReaction>): Array<EmojiReaction> =>
emojiReacts.toSorted(emojiReact => -(emojiReact.count || 0));
const simulateEmojiReact = (emojiReacts: Array<EmojiReaction>, emoji: string, url?: string) => {
const idx = emojiReacts.findIndex(e => e.name === emoji);
const emojiReact = emojiReacts[idx];
@ -44,7 +41,6 @@ const simulateUnEmojiReact = (emojiReacts: Array<EmojiReaction>, emoji: string)
};
export {
sortEmoji,
simulateEmojiReact,
simulateUnEmojiReact,
};