MM-14038 Access recent reactions from the long press menu (#4058)

* WIP: Implementing quick reaction functionality

* Making closing animations smoother

- Added haptic feedback + timeouts for a smooth flow

* Fixing clipping of post options on android

* updating snapshots

* Refactoring code & fixing styles

- Fixed paddings element sizes to comply with design specs
- Replaced reaction image to one with a higher resolution
- Made sizes of emojis to be conditional on deviceWidth

* Apply suggestions from code review

* Adding component to avoid extra re-renders

* Refactoring function call in reaction_button

Am ashamed i didn't catch this before pushing the rest

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Andre Vasconcelos 2020-04-02 04:56:49 +09:00 committed by GitHub
parent 536eb0311b
commit d03d504d00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 860 additions and 135 deletions

View file

@ -0,0 +1,529 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Reactions Should match snapshot with default emojis 1`] = `
<View
style={
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"height": 72,
"justifyContent": "space-between",
"paddingLeft": 12,
"paddingRight": 12,
}
}
>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="thumbsup"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="smiley"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="white_check_mark"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="heart"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="eyes"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="raised_hands"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<TouchableWithoutFeedback
onPress={[MockFunction]}
>
<View
style={
Array [
Object {
"alignItems": "center",
"backgroundColor": "rgba(61,60,64,0.04)",
"borderRadius": 4,
"justifyContent": "center",
},
Object {
"height": 48,
"width": 48,
},
]
}
>
<Image
height={32}
source={
Object {
"testUri": "../../../dist/assets/images/icons/reaction.png",
}
}
style={
Array [
Object {
"tintColor": "#3d3c40",
},
]
}
width={32}
/>
</View>
</TouchableWithoutFeedback>
</View>
`;
exports[`Reactions Should match snapshot with default emojis 2`] = `
<View
style={
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"height": 72,
"justifyContent": "space-between",
"paddingLeft": 12,
"paddingRight": 12,
}
}
>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="rocket"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="100"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="thumbsup"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="smiley"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="white_check_mark"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<ReactionButton
addReaction={[Function]}
containerSize={48}
emoji="heart"
iconSize={32}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<TouchableWithoutFeedback
onPress={[MockFunction]}
>
<View
style={
Array [
Object {
"alignItems": "center",
"backgroundColor": "rgba(61,60,64,0.04)",
"borderRadius": 4,
"justifyContent": "center",
},
Object {
"height": 48,
"width": 48,
},
]
}
>
<Image
height={32}
source={
Object {
"testUri": "../../../dist/assets/images/icons/reaction.png",
}
}
style={
Array [
Object {
"tintColor": "#3d3c40",
},
]
}
width={32}
/>
</View>
</TouchableWithoutFeedback>
</View>
`;

View file

@ -0,0 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getDimensions} from 'app/selectors/device';
import ReactionPicker from './reaction_picker';
function mapStateToProps(state) {
const {deviceWidth} = getDimensions(state);
return {
theme: getTheme(state),
recentEmojis: state.views.recentEmojis,
deviceWidth,
};
}
export default connect(mapStateToProps)(ReactionPicker);

View file

@ -0,0 +1,98 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
View,
TouchableWithoutFeedback,
} from 'react-native';
import Emoji from 'app/components/emoji';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {hapticFeedback} from 'app/utils/general';
import {
LARGE_CONTAINER_SIZE,
LARGE_ICON_SIZE,
} from 'app/constants/reaction_picker';
export default class ReactionButton extends PureComponent {
static propTypes = {
addReaction: PropTypes.func.isRequired,
theme: PropTypes.object.isRequired,
emoji: PropTypes.string.isRequired,
iconSize: PropTypes.number,
containerSize: PropTypes.number,
}
static defaultProps = {
iconSize: LARGE_ICON_SIZE,
containerSize: LARGE_CONTAINER_SIZE,
};
constructor(props) {
super(props);
this.state = {
isSelected: false,
};
}
handlePress = () => {
hapticFeedback();
this.setState({isSelected: true}, () => {
this.props.addReaction(this.props.emoji);
});
}
render() {
const {
theme,
emoji,
iconSize,
containerSize,
} = this.props;
const style = getStyleSheet(theme);
return (
<TouchableWithoutFeedback
key={emoji}
onPress={this.handlePress}
>
<View
style={[
style.reactionContainer,
this.state.isSelected ? style.highlight : null,
{
width: containerSize,
height: containerSize,
},
]}
>
<Emoji
emojiName={emoji}
textStyle={style.emoji}
size={iconSize}
/>
</View>
</TouchableWithoutFeedback>
);
}
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
emoji: {
color: '#000',
fontWeight: 'bold',
},
highlight: {
backgroundColor: changeOpacity(theme.linkColor, 0.1),
},
reactionContainer: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.04),
borderRadius: 4,
alignItems: 'center',
justifyContent: 'center',
},
};
});

View file

@ -0,0 +1,117 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Image,
View,
TouchableWithoutFeedback,
} from 'react-native';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import addReactionIcon from 'assets/images/icons/reaction.png';
import ReactionButton from './reaction_button';
import {
REACTION_PICKER_HEIGHT,
DEFAULT_EMOJIS,
SMALL_ICON_BREAKPOINT,
SMALL_CONTAINER_SIZE,
LARGE_CONTAINER_SIZE,
SMALL_ICON_SIZE,
LARGE_ICON_SIZE,
} from 'app/constants/reaction_picker';
export default class ReactionPicker extends PureComponent {
static propTypes = {
addReaction: PropTypes.func.isRequired,
openReactionScreen: PropTypes.func.isRequired,
theme: PropTypes.object.isRequired,
recentEmojis: PropTypes.array,
deviceWidth: PropTypes.number,
}
handlePress = (emoji) => {
this.props.addReaction(emoji);
}
render() {
const {
theme,
deviceWidth,
} = this.props;
const style = getStyleSheet(theme);
const isSmallDevice = deviceWidth < SMALL_ICON_BREAKPOINT;
let containerSize = LARGE_CONTAINER_SIZE;
let iconSize = LARGE_ICON_SIZE;
if (isSmallDevice) {
containerSize = SMALL_CONTAINER_SIZE;
iconSize = SMALL_ICON_SIZE;
}
// Mixing recent emojis with default list and removing duplicates
const emojis = Array.from(new Set(this.props.recentEmojis.concat(DEFAULT_EMOJIS))).splice(0, 6);
const list = emojis.map((emoji) => {
return (
<ReactionButton
key={emoji}
theme={theme}
addReaction={this.handlePress}
emoji={emoji}
iconSize={iconSize}
containerSize={containerSize}
/>
);
});
return (
<View style={style.reactionListContainer}>
{list}
<TouchableWithoutFeedback onPress={this.props.openReactionScreen}>
<View
style={[
style.reactionContainer,
{
width: containerSize,
height: containerSize,
},
]}
>
<Image
source={addReactionIcon}
style={[style.iconImage]}
width={iconSize}
height={iconSize}
/>
</View>
</TouchableWithoutFeedback>
</View>
);
}
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
iconImage: {
tintColor: theme.centerChannelColor,
},
reactionListContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
paddingLeft: 12,
paddingRight: 12,
height: REACTION_PICKER_HEIGHT,
justifyContent: 'space-between',
},
reactionContainer: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.04),
borderRadius: 4,
alignItems: 'center',
justifyContent: 'center',
},
};
});

View file

@ -0,0 +1,35 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import ReactionPicker from './reaction_picker';
import Preferences from 'mattermost-redux/constants/preferences';
describe('Reactions', () => {
const baseProps = {
theme: Preferences.THEMES.default,
recentEmojis: [],
addReaction: jest.fn(),
openReactionScreen: jest.fn(),
};
test('Should match snapshot with default emojis', () => {
const wrapper = shallow(<ReactionPicker {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
test('Should match snapshot with default emojis', () => {
const props = {
...baseProps,
recentEmojis: [
'rocket',
'100',
],
};
const wrapper = shallow(<ReactionPicker {...props}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -153,7 +153,7 @@ exports[`Reactions should match snapshot 1`] = `
}
style={
Object {
"height": 20,
"height": 23,
"tintColor": "rgba(61,60,64,0.5)",
"width": 23,
}

View file

@ -186,7 +186,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
addReaction: {
tintColor: changeOpacity(theme.centerChannelColor, 0.5),
width: 23,
height: 20,
height: 23,
},
reaction: {
alignItems: 'center',

View file

@ -0,0 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const REACTION_PICKER_HEIGHT = 72;
export const SMALL_ICON_BREAKPOINT = 410;
export const SMALL_CONTAINER_SIZE = 44;
export const LARGE_CONTAINER_SIZE = 48;
export const SMALL_ICON_SIZE = 28;
export const LARGE_ICON_SIZE = 32;
export const DEFAULT_EMOJIS = [
'thumbsup',
'smiley',
'white_check_mark',
'heart',
'eyes',
'raised_hands',
];

View file

@ -10,8 +10,8 @@ exports[`PostOptions should match snapshot, no option for system message to user
>
<Connect(SlideUpPanel)
allowStayMiddle={false}
initialPosition={300}
marginFromTop={300}
initialPosition={322}
marginFromTop={278}
onRequestClose={[Function]}
theme={
Object {
@ -43,6 +43,10 @@ exports[`PostOptions should match snapshot, no option for system message to user
}
}
>
<Connect(ReactionPicker)
addReaction={[Function]}
openReactionScreen={[Function]}
/>
<PostOption
destructive={false}
icon="reply"
@ -79,42 +83,6 @@ exports[`PostOptions should match snapshot, no option for system message to user
}
}
/>
<PostOption
destructive={false}
icon="emoji"
isLandscape={false}
onPress={[Function]}
text="Add Reaction"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<PostOption
icon="bookmark"
isLandscape={false}
@ -236,8 +204,8 @@ exports[`PostOptions should match snapshot, showing Delete option only for syste
>
<Connect(SlideUpPanel)
allowStayMiddle={false}
initialPosition={350}
marginFromTop={250}
initialPosition={372}
marginFromTop={228}
onRequestClose={[Function]}
theme={
Object {
@ -269,6 +237,10 @@ exports[`PostOptions should match snapshot, showing Delete option only for syste
}
}
>
<Connect(ReactionPicker)
addReaction={[Function]}
openReactionScreen={[Function]}
/>
<PostOption
destructive={false}
icon="reply"
@ -305,42 +277,6 @@ exports[`PostOptions should match snapshot, showing Delete option only for syste
}
}
/>
<PostOption
destructive={false}
icon="emoji"
isLandscape={false}
onPress={[Function]}
text="Add Reaction"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<PostOption
icon="bookmark"
isLandscape={false}
@ -498,8 +434,8 @@ exports[`PostOptions should match snapshot, showing all possible options 1`] = `
>
<Connect(SlideUpPanel)
allowStayMiddle={false}
initialPosition={350}
marginFromTop={250}
initialPosition={372}
marginFromTop={228}
onRequestClose={[Function]}
theme={
Object {
@ -531,6 +467,10 @@ exports[`PostOptions should match snapshot, showing all possible options 1`] = `
}
}
>
<Connect(ReactionPicker)
addReaction={[Function]}
openReactionScreen={[Function]}
/>
<PostOption
destructive={false}
icon="reply"
@ -567,42 +507,6 @@ exports[`PostOptions should match snapshot, showing all possible options 1`] = `
}
}
/>
<PostOption
destructive={false}
icon="emoji"
isLandscape={false}
onPress={[Function]}
text="Add Reaction"
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
<PostOption
icon="bookmark"
isLandscape={false}

View file

@ -16,6 +16,9 @@ import {showModal, dismissModal} from 'app/actions/navigation';
import {isSystemMessage} from '@mm-redux/utils/post_utils';
import {OPTION_HEIGHT, getInitialPosition} from './post_options_utils';
import {REACTION_PICKER_HEIGHT} from 'app/constants/reaction_picker';
import ReactionPicker from 'app/components/reaction_picker';
import PostOption from './post_option';
export default class PostOptions extends PureComponent {
@ -86,21 +89,6 @@ export default class PostOptions extends PureComponent {
);
}
getAddReactionOption = () => {
const {canAddReaction} = this.props;
if (canAddReaction) {
const key = 'reaction';
const icon = 'emoji';
const message = {id: t('mobile.post_info.add_reaction'), defaultMessage: 'Add Reaction'};
const onPress = this.handleAddReaction;
return this.getOption(key, icon, message, onPress);
}
return null;
};
getReplyOption = () => {
const {canReply} = this.props;
@ -249,7 +237,6 @@ export default class PostOptions extends PureComponent {
getPostOptions = () => {
const actions = [
this.getReplyOption(),
this.getAddReactionOption(),
this.getMarkAsUnreadOption(),
this.getCopyPermalink(),
this.getFlagOption(),
@ -262,7 +249,7 @@ export default class PostOptions extends PureComponent {
return actions.filter((a) => a !== null);
};
handleAddReaction = () => {
handleAddReactionScreen = () => {
const {theme} = this.props;
const {formatMessage} = this.context.intl;
@ -287,6 +274,12 @@ export default class PostOptions extends PureComponent {
});
};
handleAddReaction = (emoji) => {
this.closeWithAnimation(() => {
this.handleAddReactionToPost(emoji);
});
}
handleAddReactionToPost = (emoji) => {
const {actions, post} = this.props;
@ -402,13 +395,26 @@ export default class PostOptions extends PureComponent {
};
render() {
const {deviceHeight, theme} = this.props;
const {deviceHeight, theme, canAddReaction} = this.props;
const options = this.getPostOptions();
let reactionHeight = 0;
let reactionPicker;
if (!options || !options.length) {
return null;
}
const marginFromTop = deviceHeight - BOTTOM_MARGIN - ((options.length + 1) * OPTION_HEIGHT);
if (canAddReaction) {
reactionHeight = REACTION_PICKER_HEIGHT;
reactionPicker = (
<ReactionPicker
addReaction={this.handleAddReaction}
openReactionScreen={this.handleAddReactionScreen}
/>
);
}
const marginFromTop = deviceHeight - BOTTOM_MARGIN - ((options.length + 1) * OPTION_HEIGHT) - reactionHeight;
const initialPosition = getInitialPosition(deviceHeight, marginFromTop);
return (
@ -422,6 +428,7 @@ export default class PostOptions extends PureComponent {
key={marginFromTop}
theme={theme}
>
{reactionPicker}
{options}
</SlideUpPanel>
</View>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 929 B

After

Width:  |  Height:  |  Size: 2.1 KiB