diff --git a/app/components/post_list/post/body/reactions/reaction.tsx b/app/components/post_list/post/body/reactions/reaction.tsx
index 3d7535f90..cf3dac1fe 100644
--- a/app/components/post_list/post/body/reactions/reaction.tsx
+++ b/app/components/post_list/post/body/reactions/reaction.tsx
@@ -2,11 +2,13 @@
// See LICENSE.txt for license information.
import React, {useCallback} from 'react';
-import {Platform, Text} from 'react-native';
+import {View} from 'react-native';
+import AnimatedNumbers from 'react-native-animated-numbers';
import {TouchableOpacity} from 'react-native-gesture-handler';
import Emoji from '@components/emoji';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
+import {typography} from '@utils/typography';
type ReactionProps = {
count: number;
@@ -20,23 +22,30 @@ type ReactionProps = {
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
count: {
- color: theme.linkColor,
- marginLeft: 6,
+ color: changeOpacity(theme.centerChannelColor, 0.56),
+ ...typography('Body', 100, 'SemiBold'),
+ },
+ countContainer: {marginRight: 5},
+ countHighlight: {
+ color: theme.buttonBg,
+ },
+ customEmojiStyle: {color: '#000'},
+ emoji: {marginHorizontal: 5},
+ highlight: {
+ backgroundColor: changeOpacity(theme.buttonBg, 0.08),
+ borderColor: theme.buttonBg,
+ borderWidth: 1,
},
- customEmojiStyle: {marginHorizontal: 3},
- highlight: {backgroundColor: changeOpacity(theme.linkColor, 0.1)},
reaction: {
alignItems: 'center',
- borderRadius: 2,
- borderColor: changeOpacity(theme.linkColor, 0.4),
- borderWidth: 1,
+ borderRadius: 4,
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
flexDirection: 'row',
- height: 30,
- marginRight: 6,
- marginBottom: 5,
- marginTop: 10,
- paddingHorizontal: 6,
- paddingBottom: Platform.select({android: 2}),
+ height: 32,
+ justifyContent: 'center',
+ marginBottom: 12,
+ marginRight: 8,
+ minWidth: 50,
},
};
});
@@ -55,19 +64,22 @@ const Reaction = ({count, emojiName, highlight, onPress, onLongPress, theme}: Re
delayLongPress={350}
style={[styles.reaction, (highlight && styles.highlight)]}
>
-
-
- {count}
-
+
+
+
+
+
+
);
};
diff --git a/app/components/post_list/post/body/reactions/reactions.tsx b/app/components/post_list/post/body/reactions/reactions.tsx
index be8e22a0e..134d409fe 100644
--- a/app/components/post_list/post/body/reactions/reactions.tsx
+++ b/app/components/post_list/post/body/reactions/reactions.tsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import React, {useRef} from 'react';
+import React, {useCallback, useEffect, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {View} from 'react-native';
import {TouchableOpacity} from 'react-native-gesture-handler';
@@ -36,23 +36,22 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
reaction: {
alignItems: 'center',
justifyContent: 'center',
- borderRadius: 2,
- borderColor: changeOpacity(theme.centerChannelColor, 0.3),
- borderWidth: 1,
+ borderRadius: 4,
+ backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
flexDirection: 'row',
- height: 30,
+ height: 32,
+ marginBottom: 12,
marginRight: 6,
- marginBottom: 5,
- marginTop: 10,
- paddingVertical: 2,
+ paddingVertical: 4,
paddingHorizontal: 6,
- width: 40,
+ width: 36,
},
reactionsContainer: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
alignContent: 'flex-start',
+ marginTop: 12,
},
};
});
@@ -61,12 +60,21 @@ const Reactions = ({currentUserId, canAddReaction, canRemoveReaction, disabled,
const intl = useIntl();
const serverUrl = useServerUrl();
const pressed = useRef(false);
- if (!reactions) {
- return null;
- }
+ const [sortedReactions, setSortedReactions] = useState(new Set(reactions.map((r) => r.emojiName)));
const styles = getStyleSheet(theme);
- const buildReactionsMap = () => {
+ useEffect(() => {
+ // This helps keep the reactions in the same position at all times until unmounted
+ const rs = reactions.map((r) => r.emojiName);
+ const sorted = new Set([...sortedReactions]);
+ 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);
+ setSortedReactions(sorted);
+ }, [reactions]);
+
+ const buildReactionsMap = useCallback(() => {
const highlightedReactions: string[] = [];
const reactionsByName = reactions.reduce((acc, reaction) => {
@@ -86,7 +94,7 @@ const Reactions = ({currentUserId, canAddReaction, canRemoveReaction, disabled,
}, new Map());
return {reactionsByName, highlightedReactions};
- };
+ }, [sortedReactions]);
const handleAddReactionToPost = (emoji: string) => {
addReaction(serverUrl, postId, emoji);
@@ -148,11 +156,12 @@ const Reactions = ({currentUserId, canAddReaction, canRemoveReaction, disabled,
return (
{
- Array.from(reactionsByName.keys()).map((r) => {
+ Array.from(sortedReactions).map((r) => {
+ const reaction = reactionsByName.get(r);
return (
=16.8.0",
+ "react-native": ">=0.59",
+ "react-native-gesture-handler": "1.10.3",
+ "react-native-reanimated": ">=2"
+ }
+ },
+ "node_modules/react-native-animated-numbers/node_modules/fbjs": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz",
+ "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==",
+ "dependencies": {
+ "cross-fetch": "^3.1.5",
+ "fbjs-css-vars": "^1.0.0",
+ "loose-envify": "^1.0.0",
+ "object-assign": "^4.1.0",
+ "promise": "^7.1.1",
+ "setimmediate": "^1.0.5",
+ "ua-parser-js": "^0.7.30"
+ }
+ },
+ "node_modules/react-native-animated-numbers/node_modules/promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "dependencies": {
+ "asap": "~2.0.3"
+ }
+ },
+ "node_modules/react-native-animated-numbers/node_modules/react-native-gesture-handler": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
+ "integrity": "sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==",
+ "dependencies": {
+ "@egjs/hammerjs": "^2.0.17",
+ "fbjs": "^3.0.0",
+ "hoist-non-react-statics": "^3.3.0",
+ "invariant": "^2.2.4",
+ "prop-types": "^15.7.2"
+ }
+ },
"node_modules/react-native-background-timer": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/react-native-background-timer/-/react-native-background-timer-2.4.1.tgz",
@@ -30926,6 +30989,14 @@
"sha.js": "^2.4.8"
}
},
+ "cross-fetch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
+ "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
+ "requires": {
+ "node-fetch": "2.6.7"
+ }
+ },
"cross-spawn": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
@@ -32945,6 +33016,11 @@
}
}
},
+ "fbjs-css-vars": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz",
+ "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="
+ },
"figgy-pudding": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
@@ -39296,6 +39372,51 @@
"resolved": "https://registry.npmjs.org/react-native-android-open-settings/-/react-native-android-open-settings-1.3.0.tgz",
"integrity": "sha512-h4FTWRtTLRVNS7RK4Bg2gAXe8G5bFZL8Jtmfk2rG2a/N/wJR+v1rAY2BxRkC48lQTqwQCIAQRbWgLVkJ8XmaLQ=="
},
+ "react-native-animated-numbers": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/react-native-animated-numbers/-/react-native-animated-numbers-0.4.1.tgz",
+ "integrity": "sha512-3ZnJ5XBipdPeET4vWh8X4YinGaspb28Lp9Aw/n39r57+8ApWp+0pRFeNobG4FBo6UzBjRmMydD7q+fAC+5LZsA==",
+ "requires": {
+ "react-native-gesture-handler": "^1.10.3",
+ "react-native-reanimated": "^2.2.4"
+ },
+ "dependencies": {
+ "fbjs": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz",
+ "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==",
+ "requires": {
+ "cross-fetch": "^3.1.5",
+ "fbjs-css-vars": "^1.0.0",
+ "loose-envify": "^1.0.0",
+ "object-assign": "^4.1.0",
+ "promise": "^7.1.1",
+ "setimmediate": "^1.0.5",
+ "ua-parser-js": "^0.7.30"
+ }
+ },
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "requires": {
+ "asap": "~2.0.3"
+ }
+ },
+ "react-native-gesture-handler": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
+ "integrity": "sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==",
+ "requires": {
+ "@egjs/hammerjs": "^2.0.17",
+ "fbjs": "^3.0.0",
+ "hoist-non-react-statics": "^3.3.0",
+ "invariant": "^2.2.4",
+ "prop-types": "^15.7.2"
+ }
+ }
+ }
+ },
"react-native-background-timer": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/react-native-background-timer/-/react-native-background-timer-2.4.1.tgz",
diff --git a/package.json b/package.json
index adda418a3..3f11344a3 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"react-intl": "5.24.6",
"react-native": "0.67.3",
"react-native-android-open-settings": "1.3.0",
+ "react-native-animated-numbers": "0.4.1",
"react-native-background-timer": "2.4.1",
"react-native-button": "3.0.1",
"react-native-calendars": "1.1278.0",