mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-04 09:58:57 +00:00
* prevent crash when reaction emoji is removed while ReactionsScreen is open * change test as per copilot suggestion
98 lines
3.4 KiB
TypeScript
98 lines
3.4 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import React, {useCallback, useEffect, useMemo, useState} from 'react';
|
|
|
|
import {Screens} from '@constants';
|
|
import BottomSheet from '@screens/bottom_sheet';
|
|
import {getEmojiFirstAlias} from '@utils/emoji/helpers';
|
|
|
|
import EmojiAliases from './emoji_aliases';
|
|
import EmojiBar from './emoji_bar';
|
|
import ReactorsList from './reactors_list';
|
|
|
|
import type ReactionModel from '@typings/database/models/servers/reaction';
|
|
import type {AvailableScreens} from '@typings/screens/navigation';
|
|
|
|
type Props = {
|
|
initialEmoji: string;
|
|
location: AvailableScreens;
|
|
reactions?: ReactionModel[];
|
|
}
|
|
|
|
function getSortedReactions(reactions: ReactionModel[] | undefined, prevSortedReactions: string[]): string[] {
|
|
const rs = reactions?.map((r) => getEmojiFirstAlias(r.emojiName));
|
|
const sorted = new Set([...prevSortedReactions]);
|
|
const added = rs?.filter((r) => !sorted.has(r));
|
|
added?.forEach(sorted.add, sorted);
|
|
const removed = [...sorted].filter((s) => !rs?.includes(s));
|
|
removed.forEach(sorted.delete, sorted);
|
|
return Array.from(sorted);
|
|
}
|
|
|
|
const Reactions = ({initialEmoji, location, reactions}: Props) => {
|
|
const [sortedReactions, setSortedReactions] = useState(Array.from(new Set(reactions?.map((r) => getEmojiFirstAlias(r.emojiName)))));
|
|
const [index, setIndex] = useState(() => Math.max(0, sortedReactions.indexOf(initialEmoji)));
|
|
|
|
const reactionsByName = useMemo(() => {
|
|
return reactions?.reduce((acc, reaction) => {
|
|
const emojiAlias = getEmojiFirstAlias(reaction.emojiName);
|
|
if (acc.has(emojiAlias)) {
|
|
const rs = acc.get(emojiAlias);
|
|
// eslint-disable-next-line max-nested-callbacks
|
|
const present = rs!.findIndex((r) => r.userId === reaction.userId) > -1;
|
|
if (!present) {
|
|
rs!.push(reaction);
|
|
}
|
|
} else {
|
|
acc.set(emojiAlias, [reaction]);
|
|
}
|
|
|
|
return acc;
|
|
}, new Map<string, ReactionModel[]>());
|
|
}, [reactions]);
|
|
|
|
const renderContent = useCallback(() => {
|
|
const emojiAlias = sortedReactions[index];
|
|
if (!emojiAlias || !reactionsByName) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<EmojiBar
|
|
emojiSelected={emojiAlias}
|
|
reactionsByName={reactionsByName}
|
|
setIndex={setIndex}
|
|
sortedReactions={sortedReactions}
|
|
/>
|
|
<EmojiAliases emoji={emojiAlias}/>
|
|
<ReactorsList
|
|
key={emojiAlias}
|
|
location={location}
|
|
reactions={reactionsByName.get(emojiAlias)!}
|
|
/>
|
|
</>
|
|
);
|
|
}, [index, location, reactionsByName, sortedReactions]);
|
|
|
|
useEffect(() => {
|
|
// This helps keep the reactions in the same position at all times until unmounted
|
|
setSortedReactions((prevSortedReactions) => {
|
|
return getSortedReactions(reactions, prevSortedReactions);
|
|
});
|
|
}, [reactions]);
|
|
|
|
return (
|
|
<BottomSheet
|
|
renderContent={renderContent}
|
|
screen={Screens.REACTIONS}
|
|
initialSnapIndex={1}
|
|
snapPoints={[1, '50%', '80%']}
|
|
testID='reactions'
|
|
/>
|
|
);
|
|
};
|
|
|
|
export default Reactions;
|